switched from PracticalSocket to libasio
[anytun.git] / src / Sockets / Ajp13Socket.cpp
blob57755f07a19a3b8f15971bd328e75346566abab3
1 /**
2 ** \file Ajp13Socket.cpp
3 ** \date 2007-10-05
4 ** \author grymse@alhem.net
5 **/
6 /*
7 Copyright (C) 2007 Anders Hedstrom
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 #ifdef _MSC_VER
24 #pragma warning(disable:4786)
25 #endif
26 #include "Ajp13Socket.h"
27 #include "ajp13.h"
28 #include "HttpRequest.h"
29 #include "HttpResponse.h"
30 #include "IFile.h"
31 #include "Utility.h"
33 #ifdef SOCKETS_NAMESPACE
34 namespace SOCKETS_NAMESPACE {
35 #endif
37 #ifdef _DEBUG
38 #define DEB(x) x
39 #else
40 #define DEB(x)
41 #endif
44 // --------------------------------------------------------------------------------------
45 Ajp13Socket::Ajp13Socket(ISocketHandler& h) : AjpBaseSocket(h)
46 , m_body_size_left(0)
47 , m_res_file(NULL)
52 // --------------------------------------------------------------------------------------
53 void Ajp13Socket::OnHeader( short id, short len )
55 if (id != 0x1234)
57 fprintf(stderr, "ABORT: bad packet id: %x\n", id);
58 SetCloseAndDelete();
60 else
62 DEB(fprintf(stderr, "Packet size: %d bytes\n", len);)
67 // --------------------------------------------------------------------------------------
68 void Ajp13Socket::ReceiveBody(const char *buf, size_t sz)
70 if (sz - 2 > m_body_size_left)
72 fprintf(stderr, "More body data received than expected\n");
73 SetCloseAndDelete();
74 return;
77 m_req.Write( buf + 2, sz - 2 );
78 m_body_size_left -= sz - 2;
80 // request more body data
81 if (m_body_size_left)
83 int ptr = 4;
84 char msg[100];
85 msg[0] = 'A';
86 msg[1] = 'B';
88 // reply codes
89 // 0x3 Send Body Chunk
90 // 0x4 Send Headers
91 // 0x5 End Response
92 // 0x6 Get Body Chunk <------
93 // 0x9 CPong Reply
95 put_byte(msg, ptr, 0x06); // GET_BODY_CHUNK;
96 put_integer(msg, ptr, 1000); // request 1000 bytes
98 short len = htons( ptr - 4 );
99 memcpy( msg + 2, &len, 2 );
101 SendBuf( msg, ptr );
102 return;
105 // Close
106 m_req.CloseBody();
108 // no more body data left to read - execute
109 Execute();
114 // --------------------------------------------------------------------------------------
115 void Ajp13Socket::ReceiveForwardRequest( const char *buf, size_t sz )
118 int ptr = 0;
120 get_byte(buf, ptr); // skip first byte: prefix_code
121 unsigned char method = get_byte(buf, ptr);
122 std::string protocol = get_string(buf, ptr);
123 std::string req_uri = get_string(buf, ptr);
124 std::string remote_addr = get_string(buf, ptr);
125 std::string remote_host = get_string(buf, ptr);
126 std::string server_name = get_string(buf, ptr);
127 short server_port = get_integer(buf, ptr);
128 bool is_ssl = get_boolean(buf, ptr);
130 std::string method_str = Utility::l2string( method );
131 std::map<int, std::string>::const_iterator it = Init.Method.find( method );
132 if (it != Init.Method.end())
134 method_str = it -> second;
136 m_req.SetHttpMethod( method_str );
137 m_req.SetHttpVersion( protocol );
138 m_req.SetUri( req_uri );
139 m_req.SetRemoteAddr( remote_addr );
140 m_req.SetRemoteHost( remote_host );
141 m_req.SetServerName( server_name );
142 m_req.SetServerPort( server_port );
143 m_req.SetIsSsl( is_ssl );
145 // Get Headers
146 short num_headers = get_integer(buf, ptr);
147 for (int i = 0; i < num_headers; i++)
149 std::string key;
150 switch ( (unsigned char)buf[ptr]) // 0xa0
152 case 0xa0:
154 unsigned short x = (unsigned short)get_integer(buf, ptr);
155 std::map<int, std::string>::const_iterator it;
156 if ( (it = Init.Header.find(x)) != Init.Header.end())
158 key = it -> second;
160 else
162 fprintf(stderr, "Unknown header key value: %x\n", x);
163 SetCloseAndDelete();
166 break;
168 default: // string
169 key = get_string(buf, ptr);
171 if (Utility::ToLower(key) == "cookie" || Utility::ToLower(key) == "cookie2")
172 m_req.AddCookie(get_string(buf, ptr));
173 else
174 m_req.SetHeader(key, get_string(buf, ptr));
175 } // for
177 // size left to read from web server
178 m_body_size_left = m_req.ContentLength();
180 // Get Attributes
181 while ( (unsigned char)buf[ptr] != 0xff)
183 std::string key;
184 unsigned char code = buf[ptr++];
185 switch ( code)
187 case 10: // req_attribute, attribute name follow
188 key = get_string(buf, ptr);
189 break;
190 default:
192 std::map<int, std::string>::const_iterator it = Init.Attribute.find( code );
193 if (it != Init.Attribute.end())
195 key = it -> second;
197 else
199 fprintf(stderr, "Unknown attribute key: 0x%02x\n", buf[ptr]);
200 SetCloseAndDelete();
204 m_req.SetAttribute(key, get_string(buf, ptr));
205 } // while
207 // execute at once if no body data
208 if (!m_body_size_left)
210 Execute();
212 else
214 // open temporary file for body data
215 m_req.InitBody( m_body_size_left );
220 // --------------------------------------------------------------------------------------
221 void Ajp13Socket::ReceiveShutdown( const char *buf, size_t sz )
226 // --------------------------------------------------------------------------------------
227 void Ajp13Socket::ReceivePing( const char *buf, size_t sz )
232 // --------------------------------------------------------------------------------------
233 void Ajp13Socket::ReceiveCPing( const char *buf, size_t sz )
238 // --------------------------------------------------------------------------------------
239 void Ajp13Socket::Execute()
241 // parse form data / query_string and cookie header if available
242 m_req.ParseBody();
244 // prepare page
245 OnExec( m_req );
250 // --------------------------------------------------------------------------------------
251 void Ajp13Socket::Respond(const HttpResponse& res)
253 char msg[8192];
254 msg[0] = 'A';
255 msg[1] = 'B';
257 // reply codes
258 // 0x3 Send Body Chunk
259 // 0x4 Send Headers
260 // 0x5 End Response
261 // 0x6 Get Body Chunk
262 // 0x9 CPong Reply
264 // check content length
265 if (!res.ContentLength() && res.GetFile().size())
267 // res.SetContentLength( res.GetFile().size() );
270 // Send Headers
272 int ptr = 4;
273 put_byte(msg, ptr, 0x04); // send headers
274 put_integer(msg, ptr, res.HttpStatusCode() );
275 put_string(msg, ptr, res.HttpStatusMsg() );
276 put_integer(msg, ptr, (short)res.Headers().size() );
277 for (std::map<std::string, std::string>::const_iterator it = res.Headers().begin(); it != res.Headers().end(); ++it)
279 std::map<std::string, int>::const_iterator it2 = Init.ResponseHeader.find( it -> first );
280 if (it2 != Init.ResponseHeader.end())
282 put_integer(msg, ptr, it2 -> second);
284 else
286 put_string(msg, ptr, it -> first);
288 put_string(msg, ptr, it -> second);
290 std::list<std::string> vec = res.CookieNames();
292 for (std::list<std::string>::iterator it = vec.begin(); it != vec.end(); it++)
294 std::map<std::string, int>::const_iterator it2 = Init.ResponseHeader.find( "set-cookie" );
295 if (it2 != Init.ResponseHeader.end())
297 put_integer(msg, ptr, it2 -> second);
299 else
301 put_string(msg, ptr, "set-cookie");
303 put_string(msg, ptr, res.Cookie(*it) );
307 short len = htons( ptr - 4 );
308 memcpy( msg + 2, &len, 2 );
310 SendBuf( msg, ptr );
312 m_res_file = &res.GetFile();
313 // Send Body Chunk
314 OnTransferLimit();
318 // --------------------------------------------------------------------------------------
319 void Ajp13Socket::OnTransferLimit()
321 char msg[8192];
322 msg[0] = 'A';
323 msg[1] = 'B';
325 // Send Body Chunk
326 size_t n = m_res_file -> fread(msg + 7, 1, 8100);
327 while (n > 0)
329 int ptr = 4;
330 put_byte(msg, ptr, 0x03); // send body chunk
331 put_integer(msg, ptr, (short)n);
332 ptr += (int)n;
334 short len = htons( ptr - 4 );
335 memcpy( msg + 2, &len, 2 );
337 SendBuf( msg, ptr );
338 if (GetOutputLength() > 1)
340 SetTransferLimit( 1 );
341 break;
345 n = m_res_file -> fread(msg + 7, 1, 8100);
347 if (!GetOutputLength()) // all body data sent and no data in output buffer - send end response
349 // End Response
350 int ptr = 4;
351 put_byte(msg, ptr, 0x05); // end response
352 put_boolean(msg, ptr, false); // reuse
354 don't reuse
355 - but with m_req.Reset() and res.Reset() it should be possible
356 - also reset any AjpBaseSocket/Ajp13Socket specific states
359 short len = htons( ptr - 4 );
360 memcpy( msg + 2, &len, 2 );
362 SendBuf( msg, ptr );
367 // --------------------------------------------------------------------------------------
368 void Ajp13Socket::OnPacket( const char *buf, size_t sz )
370 DEB(fprintf(stderr, "OnPacket: %d bytes, code 0x%02x %02x %02x %02x\n", sz, *buf, buf[1], buf[2], buf[3]);)
372 // check body size left to read, if non-zero packet is body data
373 if (m_body_size_left) // must be a body packet
375 ReceiveBody(buf, sz);
376 return;
378 switch (*buf)
380 case 0x2: // Forward Request
381 ReceiveForwardRequest(buf, sz);
382 break;
383 case 0x7: // Shutdown
384 ReceiveShutdown(buf, sz);
385 break;
386 case 0x8: // Ping
387 ReceivePing(buf, sz);
388 break;
389 case 0xa: // CPing
390 ReceiveCPing(buf, sz);
391 break;
392 default:
393 fprintf(stderr, "Unknown packet type: 0x%02x\n", *buf);
394 SetCloseAndDelete();
400 #ifdef SOCKETS_NAMESPACE
401 } // namespace SOCKETS_NAMESPACE {
402 #endif