httpsrv.cpp

Go to the documentation of this file.
00001 
00002  //
00003  // XMLStorage XML-RPC server 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 "httpsrv.h"
00038 
00039 
00040 #ifdef _DEBUG
00041 #define HTML_FORMAT FORMAT_PRETTY
00042 #define HTML_PRETTY PRETTY_LINEFEED
00043 #else
00044 #define HTML_FORMAT FORMAT_PLAIN
00045 #define HTML_PRETTY PRETTY_PLAIN
00046 #endif
00047 
00048 
00049 #ifdef USE_HTTPAPI
00050 
00051 #include <http.h>
00052 #pragma comment(lib, "httpapi")
00053 
00054 
00055 HttpServer::HttpServer()
00056  :  _extensions(false)
00057 {
00058     HTTPAPI_VERSION version = HTTPAPI_VERSION_1;
00059 
00060     ULONG ret = HttpInitialize(version, HTTP_INITIALIZE_SERVER, NULL);
00061     if (ret)
00062         throw WE(ret);
00063 
00064     ret = HttpCreateHttpHandle(&_request, 0);
00065     if (ret)
00066         throw WE(ret);
00067 }
00068 
00069 HttpServer::~HttpServer()
00070 {
00071     CloseHandle(_request);
00072 
00073     HttpTerminate(HTTP_INITIALIZE_SERVER, NULL);
00074 }
00075 
00076 
00077 void HttpServer::register_url(LPCWSTR prefix)
00078 {
00079     ULONG ret = HttpAddUrl(_request, prefix, NULL);
00080     if (ret)
00081         throw WE(ret);
00082 }
00083 
00084 void HttpServer::unregister_url(LPCWSTR prefix)
00085 {
00086     ULONG ret = HttpRemoveUrl(_request, prefix);
00087     if (ret)
00088         throw WE(ret);
00089 }
00090 
00091 
00092 struct HttpResponse : public HTTP_RESPONSE
00093 {
00094     HttpResponse(u_short code, const char* status_text)
00095     {
00096         memset(this, 0, sizeof(HTTP_RESPONSE));
00097 
00098         StatusCode = code;
00099 
00100         ReasonLength = strlen(status_text);
00101         pReason = status_text;
00102     }
00103 
00104     void inline set_header(HTTP_HEADER_ID id, const char* str)
00105     {
00106         Headers.KnownHeaders[id].RawValueLength = strlen(str);
00107         Headers.KnownHeaders[id].pRawValue = str;
00108     }
00109 };
00110 
00111 struct HttpData : public HTTP_DATA_CHUNK
00112 {
00113     template<typename T> HttpData(const T& str)
00114     {
00115         DataChunkType = HttpDataChunkFromMemory;
00116         FromMemory.pBuffer = (void*)str.c_str();
00117         FromMemory.BufferLength = str.length();
00118     }
00119 };
00120 
00121 
00122 void HttpServer::receive_request(HttpRequest& request)
00123 {
00124     ULONG buffer_len = 2048;
00125     PHTTP_REQUEST pReq = (PHTTP_REQUEST) alloca(buffer_len);
00126 
00127     const HTTP_REQUEST_ID req_id = HTTP_NULL_ID;
00128 
00129     ULONG received;
00130     ULONG ret = HttpReceiveHttpRequest(_request, req_id, 0, pReq, buffer_len, &received, NULL);
00131 
00132     if (ret == ERROR_MORE_DATA) {
00133         buffer_len = received;
00134         pReq = (PHTTP_REQUEST) alloca(buffer_len);
00135 
00136         ret = HttpReceiveHttpRequest(_request, req_id, 0, pReq, buffer_len, &received, NULL);
00137     }
00138 
00139     if (ret)
00140         throw WE(ret);
00141 
00142     request._id = pReq->RequestId;
00143 
00144     if (pReq->Flags & HTTP_REQUEST_FLAG_MORE_ENTITY_BODY_EXISTS) {
00145         char buffer[BUFFER_LEN];
00146         buffer_len = sizeof(buffer);
00147 
00148         ret = HttpReceiveRequestEntityBody(_request, pReq->RequestId, 0, buffer, buffer_len, &received, NULL);
00149         //TODO handle larger input data
00150         if (ret)
00151             throw WE(ret);
00152 
00153         request._data.assign(buffer, received);
00154     }
00155 
00156     if (pReq->Verb == HttpVerbGET)
00157         request._command = HTTP_GET;
00158     else if (pReq->Verb == HttpVerbPOST)
00159         request._command = HTTP_POST;
00160     else
00161         request._command = HTTP_UNKNOWN;
00162 }
00163 
00164 
00165 void HttpServer::send_response(HttpRequest& request, const string& str, const char* content_type)
00166 {
00167     HttpResponse response(200, "OK");
00168 
00169     response.set_header(HttpHeaderServer, "XMLStorage XMLRPC");
00170     response.set_header(HttpHeaderContentType, content_type);
00171 
00172     HttpData data(str);
00173 
00174     response.EntityChunkCount = 1;
00175     response.pEntityChunks = &data;
00176 
00177     ULONG ret = HttpSendHttpResponse(_request, request._id, HTTP_SEND_RESPONSE_FLAG_DISCONNECT,
00178                                      &response, NULL, NULL, NULL, 0, NULL, NULL);
00179     if (ret)
00180         throw WE(ret);
00181 }
00182 
00183 void HttpServer::send_error(HttpRequest& request, u_short code, const char* status_text)
00184 {
00185     HttpResponse response(code, status_text);
00186 
00187     response.set_header(HttpHeaderServer, "XMLStorage XMLRPC");
00188     response.set_header(HttpHeaderContentType, "text/plain");
00189 
00190     char buffer[BUFFER_LEN];
00191     sprintf(buffer, "%d - %s", code, status_text);
00192     string error_msg = buffer;
00193 
00194     HttpData data(error_msg);
00195 
00196     response.EntityChunkCount = 1;
00197     response.pEntityChunks = &data;
00198 
00199     ULONG ret = HttpSendHttpResponse(_request, request._id, HTTP_SEND_RESPONSE_FLAG_DISCONNECT,
00200                                      &response, NULL, NULL, NULL, 0, NULL, NULL);
00201     if (ret)
00202         throw WE(ret);
00203 }
00204 
00205 
00206 #else // USE_HTTPAPI
00207 
00208 
00209 HttpServer::HttpServer()
00210  :  _extensions(false)
00211 {
00212 }
00213 
00214 HttpServer::~HttpServer()
00215 {
00216 }
00217 
00218 
00219 void HttpServer::register_url(const char* prefix)
00220 {
00221     URL parsed_url(prefix);
00222 
00223     if (!parsed_url._hostname.empty() && parsed_url._hostname!="+")
00224          // initialize address with hostname and port
00225         _addr.resolve(parsed_url._hostname.c_str(), parsed_url._port.c_str(), 0);
00226     else
00227          // initialize address only with port assignment
00228         _addr.resolve(NULL, parsed_url._port.c_str(), AI_PASSIVE);
00229 
00230     _url_prefix = parsed_url._path;
00231 
00232     _listen_socket.bind(_addr);
00233 }
00234 
00235 void HttpServer::unregister_url(const char* prefix)
00236 {
00237     URL parsed_url(prefix);
00238 
00239     if (parsed_url._path == _url_prefix) {
00240         _listen_socket.close();
00241         _addr.clear();
00242         _url_prefix.erase();
00243     }
00244 }
00245 
00246 
00247 void HttpServer::receive_request(HttpRequest& request)
00248 {
00249     do {
00250         if (_conn_socket == INVALID_SOCKET) {
00251             if (listen(_listen_socket, 4) < 0)
00252                 throw LWSE();
00253 
00254             SOCKADDR_IN sin;
00255             socklen_t sin_len = sizeof(sin);
00256 
00257             SOCKET accept_socket = accept(_listen_socket, (struct sockaddr*)&sin, &sin_len);
00258 
00259             if (accept_socket != INVALID_SOCKET)
00260                 _conn_socket.init(accept_socket);
00261         }
00262     } while(!handle_request(request));
00263 }
00264 
00265 bool HttpServer::handle_request(HttpRequest& request)
00266 {
00267     SocketReader reader(_conn_socket);
00268 
00269      // read the first request line
00270     char line[BUFFER_LEN];
00271 
00272     if (reader.read_line(line, BUFFER_LEN) <= 0) {
00273         // The client connection has been closed.
00274         _conn_socket.close();
00275         return false;
00276     }
00277 
00278     char command[32];
00279     char path[BUFFER_LEN];
00280     char version[12];
00281 
00282 #ifdef __STDC_WANT_SECURE_LIB__
00283     if (sscanf_s(line, "%s %s %s", command, _countof(command), path, _countof(path), version, _countof(version)) != 3)
00284 #else
00285     if (sscanf(line, "%s %s %s", command, path, version) != 3)
00286 #endif
00287         throw Exception("invalid HTML server response");
00288 
00289     if (!_stricmp(command, "GET"))
00290         request._command = HTTP_GET;
00291     else if (!_stricmp(command, "POST"))
00292         request._command = HTTP_POST;
00293     else
00294         request._command = HTTP_UNKNOWN;
00295 
00296 
00297      // read the request header
00298     int content_length = 0;
00299     string content_type;
00300 //  string host;
00301 
00302     for(;;) {
00303         if (reader.read_line(line, BUFFER_LEN) <= 0)
00304             break;
00305 
00306         if (!_strnicmp(line, "Content-length:", 15))
00307             content_length = atoi(line+15);
00308         else if (!_strnicmp(line, "Content-type:", 13))
00309             content_type = line+14;
00310 //      else if (!_strnicmp(line, "Host:", 5))
00311 //          host = line+6;
00312     }
00313 
00314     if (!content_type.empty() && _strnicmp(content_type.c_str(),"text/xml",8) &&
00315         _strnicmp(content_type.c_str(),"application/x-www-form-urlencoded",33)) {
00316         char b[BUFFER_LEN];
00317         sprintf(b, "illegal content type: %s", content_type.c_str());
00318         throw Exception(b);
00319     }
00320 
00321     if (content_length) {
00322         request._data.resize(content_length);
00323         reader.read(&request._data.at(0), content_length);
00324     }
00325 
00326     return true;
00327 }
00328 
00329 
00330 void HttpServer::send_response(HttpRequest& request, const string& str, const char* content_type)
00331 {
00332     send(200, "OK", str, content_type, !_extensions);
00333 }
00334 
00335 void HttpServer::send_error(HttpRequest& request, u_short code, const char* status_text)
00336 {
00337     char buffer[BUFFER_LEN];
00338     sprintf(buffer, "%d - %s", code, status_text);
00339     string error_msg = buffer;
00340 
00341     send(code, status_text, error_msg, "text/plain", true);
00342 }
00343 
00344 
00345 void HttpServer::send(u_short code, const char* status_text, const string& data, const char* content_type, bool connection_close)
00346 {
00347      // send the HTML response header
00348     ostringstream buffer;
00349 
00350     buffer << "HTTP/1.1 200 OK\r\n";
00351     buffer << "Server: XMLStorage XMLRPC\r\n";
00352     buffer << "Content-length: " << data.length() << "\r\n";
00353     buffer << "Content-type: " << content_type << "\r\n";
00354 //  buffer << "Date: ...\r\n";
00355 
00356     if (connection_close)
00357         buffer << "Connection: close\r\n";
00358 
00359     buffer << "\r\n";
00360 
00361     const string& header = buffer.str();
00362     _conn_socket.send(header.c_str(), header.length());
00363 
00364      // send the response body
00365     _conn_socket.send(data.c_str(), data.length());
00366 
00367     if (connection_close)
00368         _conn_socket.close();
00369 }
00370 
00371 #endif // USE_HTTPAPI
00372 
00373 
00374 struct HTMLFormat : public XMLFormat
00375 {
00376     HTMLFormat()
00377     {
00378         _pretty = HTML_PRETTY;
00379 
00380         _doctype._name = "html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" "
00381                          "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"";
00382     }
00383 };
00384 
00385 struct HTMLWriter : public XMLWriter
00386 {
00387     HTMLWriter(ostream& out)
00388      :  XMLWriter(out, HTMLFormat())
00389     {
00390     }
00391 
00392 private:
00393     ostringstream   _buffer;
00394 };
00395 
00396 
00397 static string get_html_page(XMLDoc& html_doc)
00398 {
00399     ostringstream ostr;
00400 
00401     html_doc._format._doctype._name =
00402         "html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" "
00403         "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"";
00404 
00405     html_doc.write(ostr, HTML_FORMAT);
00406 
00407     return ostr.str();
00408 }
00409 
00410 
00411 /*
00412 static void test_HttpServer(HttpServer& server)
00413 {
00414     for(;;) {
00415         HttpRequest request;
00416 
00417         server.receive(request);
00418 
00419         if (request._command == HTTP_GET) {
00420              // create a HTML page as response using XMLDoc
00421             XMLDoc html;
00422             XMLPos pos(&html);
00423 
00424             pos.create("html");
00425                 pos.create("head");
00426                     pos->set_sub_content("title", "XMLStorage XMLRPC GET Response");
00427                 pos.back();
00428 
00429                 pos.create("body");
00430                     pos->set_content("Hello XML-RPC World!");
00431 
00432 #ifdef _DEBUG
00433                     pos.create("form");
00434                         pos["method"] = "POST";
00435                         pos["name"] = "form1";
00436 
00437                         pos.create("input");
00438                             pos["type"] = "text";
00439                             pos["name"] = "text-input";
00440                         //  pos["value"] = "";
00441                         pos.back();
00442 
00443                         pos.create("input");
00444                             pos["type"] = "submit";
00445                             pos["value"] = "POST";
00446                         pos.back();
00447                     pos.back();
00448 #endif
00449                 pos.back();
00450             pos.back();
00451 
00452             server.send_response(request, get_html_page(html), "text/html");
00453         } else if (request._command == HTTP_POST) {
00454              // create a HTML page using HTMLWriter
00455             ostringstream buffer;
00456 
00457             {
00458                 HTMLWriter writer(buffer);
00459 
00460                 writer.create("html");
00461                     writer.create("head");
00462                         writer.create("title");
00463                             writer.set_content("XMLStorage XMLRPC POST Response");
00464                         writer.back();
00465                     writer.back();
00466 
00467                     writer.create("body");
00468                         string data = "Content: " + request._data;
00469                         writer.set_content(data);
00470 
00471                         writer.create("p");
00472                             writer.set_content("POST RESPONSE");
00473                         writer.back();
00474                     writer.back();
00475                 writer.back();
00476             }
00477 
00478             server.send_response(request, buffer.str(), "text/html");
00479         } else
00480             server.send_error(request, 405, "unknown or unhandled HTTP command");
00481             //throw exception("unknown HTTP command");
00482     }
00483 }
00484 
00485 
00486 #include <iostream>
00487 
00488 int main()
00489 {
00490     try {
00491         HttpServer server;
00492 
00493 //      server.register_url("http://localhost:8080/");
00494         server.register_url("http://+:8080/");
00495 
00496         test_HttpServer(server);
00497 
00498         server.unregister_url("http://+:8080/");
00499     } catch(exception& e) {
00500         cerr << e.what() << endl;
00501     }
00502 
00503     return 0;
00504 }
00505 */