1 // ----------------------------------------------------------------------------
2 //
3 // flxmlrpc Copyright (c) 2015 by W1HKJ, Dave Freese <iam_w1hkj@w1hkj.com>
4 //
5 // XmlRpc++ Copyright (c) 2002-2008 by Chris Morley
6 //
7 // This file is part of fldigi
8 //
9 // flxmlrpc is free software; you can redistribute it and/or modify
10 // it under the terms of the GNU Lesser General Public License as published by
11 // the Free Software Foundation; either version 3 of the License, or
12 // (at your option) any later version.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ----------------------------------------------------------------------------
17
18 #include <config.h>
19
20 #include "XmlRpcServerConnection.h"
21
22 #include "XmlRpcDispatch.h"
23 #include "XmlRpcServer.h"
24 #include "XmlRpcSocket.h"
25 #include "XmlRpcUtil.h"
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <strings.h>
31
32 using namespace XmlRpc;
33
34
35
36 // The server delegates handling client requests to a serverConnection object.
XmlRpcServerConnection(XmlRpcSocket::Socket fd,XmlRpcServer * server,bool deleteOnClose)37 XmlRpcServerConnection::XmlRpcServerConnection(XmlRpcSocket::Socket fd,
38 XmlRpcServer* server,
39 bool deleteOnClose /*= false*/) :
40 XmlRpcSource(fd, deleteOnClose)
41 {
42 XmlRpcUtil::log(2,"XmlRpcServerConnection: new socket %d.", fd);
43 _server = server;
44 _connectionState = READ_HEADER;
45 _keepAlive = true;
46 }
47
48
~XmlRpcServerConnection()49 XmlRpcServerConnection::~XmlRpcServerConnection()
50 {
51 XmlRpcUtil::log(4,"XmlRpcServerConnection dtor.");
52 _server->removeConnection(this);
53 }
54
55
56 // Handle input on the server socket by accepting the connection
57 // and reading the rpc request. Return true to continue to monitor
58 // the socket for events, false to remove it from the dispatcher.
59 unsigned
handleEvent(unsigned)60 XmlRpcServerConnection::handleEvent(unsigned /*eventType*/)
61 {
62 if (_connectionState == READ_HEADER)
63 if ( ! readHeader()) return 0;
64
65 if (_connectionState == READ_REQUEST)
66 if ( ! readRequest()) return 0;
67
68 if (_connectionState == WRITE_RESPONSE)
69 if ( ! writeResponse()) return 0;
70
71 return (_connectionState == WRITE_RESPONSE)
72 ? XmlRpcDispatch::WritableEvent : XmlRpcDispatch::ReadableEvent;
73 }
74
75
76 bool
readHeader()77 XmlRpcServerConnection::readHeader()
78 {
79 // Read available data
80 bool eof;
81 if ( ! nbRead(_header, &eof))
82 {
83 // Its only an error if we already have read some data
84 if (_header.length() > 0)
85 XmlRpcUtil::error("XmlRpcServerConnection::readHeader: error while reading header (%s).",XmlRpcSocket::getErrorMsg().c_str());
86 return false;
87 }
88
89 XmlRpcUtil::log(4, "XmlRpcServerConnection::readHeader: read %d bytes.", _header.length());
90 char *hp = (char*)_header.c_str(); // Start of header
91 char *ep = hp + _header.length(); // End of string
92 char *bp = 0; // Start of body
93 char *lp = 0; // Start of content-length value
94 char *kp = 0; // Start of connection value
95
96 for (char *cp = hp; (bp == 0) && (cp < ep); ++cp) {
97 if ((ep - cp > 16) && (strncasecmp(cp, "Content-length: ", 16) == 0))
98 lp = cp + 16;
99 else if ((ep - cp > 12) && (strncasecmp(cp, "Connection: ", 12) == 0))
100 kp = cp + 12;
101 else if ((ep - cp > 4) && (strncmp(cp, "\r\n\r\n", 4) == 0))
102 bp = cp + 4;
103 else if ((ep - cp > 2) && (strncmp(cp, "\n\n", 2) == 0))
104 bp = cp + 2;
105 }
106
107 // If we haven't gotten the entire header yet, return (keep reading)
108 if (bp == 0) {
109 // EOF in the middle of a request is an error, otherwise its ok
110 if (eof) {
111 XmlRpcUtil::log(4, "XmlRpcServerConnection::readHeader: EOF");
112 if (_header.length() > 0)
113 XmlRpcUtil::error("XmlRpcServerConnection::readHeader: EOF while reading header");
114 return false; // Either way we close the connection
115 }
116
117 return true; // Keep reading
118 }
119
120 // Decode content length
121 if (lp == 0) {
122 XmlRpcUtil::error("XmlRpcServerConnection::readHeader: No Content-length specified");
123 return false; // We could try to figure it out by parsing as we read, but for now...
124 }
125
126 _contentLength = atoi(lp);
127 if (_contentLength <= 0) {
128 XmlRpcUtil::error("XmlRpcServerConnection::readHeader: Invalid Content-length specified (%d).", _contentLength);
129 return false;
130 }
131
132 XmlRpcUtil::log(3, "XmlRpcServerConnection::readHeader: specified content length is %d.", _contentLength);
133
134 // Otherwise copy non-header data to request buffer and set state to read request.
135 _request = bp;
136
137 // Parse out any interesting bits from the header (HTTP version, connection)
138 _keepAlive = true;
139 if (_header.find("HTTP/1.0") != std::string::npos) {
140 if (kp == 0 || strncasecmp(kp, "keep-alive", 10) != 0)
141 _keepAlive = false; // Default for HTTP 1.0 is to close the connection
142 } else {
143 if (kp != 0 && strncasecmp(kp, "close", 5) == 0)
144 _keepAlive = false;
145 }
146 XmlRpcUtil::log(3, "KeepAlive: %d", _keepAlive);
147
148
149 _header = "";
150 _connectionState = READ_REQUEST;
151 return true; // Continue monitoring this source
152 }
153
154
155
156 bool
readRequest()157 XmlRpcServerConnection::readRequest()
158 {
159 // If we dont have the entire request yet, read available data
160 if (int(_request.length()) < _contentLength)
161 {
162 bool eof;
163 if ( ! nbRead(_request, &eof))
164 {
165 XmlRpcUtil::error("XmlRpcServerConnection::readRequest: read error (%s).",XmlRpcSocket::getErrorMsg().c_str());
166 return false;
167 }
168
169 // If we haven't gotten the entire request yet, return (keep reading)
170 if (int(_request.length()) < _contentLength)
171 {
172 if (eof)
173 {
174 XmlRpcUtil::error("XmlRpcServerConnection::readRequest: EOF while reading request");
175 return false; // Either way we close the connection
176 }
177 return true;
178 }
179 }
180
181 // Otherwise, parse and dispatch the request
182 XmlRpcUtil::log(3, "XmlRpcServerConnection::readRequest read %d bytes.", _request.length());
183 //XmlRpcUtil::log(5, "XmlRpcServerConnection::readRequest:\n%s\n", _request.c_str());
184
185 _connectionState = WRITE_RESPONSE;
186
187 return true; // Continue monitoring this source
188 }
189
190
191
192 bool
writeResponse()193 XmlRpcServerConnection::writeResponse()
194 {
195 if (_response.length() == 0)
196 {
197 executeRequest();
198 _bytesWritten = 0;
199 if (_response.length() == 0)
200 {
201 XmlRpcUtil::error("XmlRpcServerConnection::writeResponse: empty response.");
202 return false;
203 }
204 }
205
206 // Try to write the response
207 if ( ! nbWrite(_response, &_bytesWritten))
208 {
209 XmlRpcUtil::error("XmlRpcServerConnection::writeResponse: write error (%s).",XmlRpcSocket::getErrorMsg().c_str());
210 return false;
211 }
212 XmlRpcUtil::log(3, "XmlRpcServerConnection::writeResponse: wrote %d of %d bytes.", _bytesWritten, _response.length());
213
214 // Prepare to read the next request
215 if (_bytesWritten == int(_response.length()))
216 {
217 _header = "";
218 _request = "";
219 _response = "";
220 _connectionState = READ_HEADER;
221 }
222
223 return _keepAlive; // Continue monitoring this source if true
224 }
225
226
227 //! Helper method to execute the client request
executeRequest()228 void XmlRpcServerConnection::executeRequest()
229 {
230 _response = _server->executeRequest(_request);
231 }
232
233