summaryrefslogtreecommitdiff
path: root/src/Sockets/Ajp13Socket.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Sockets/Ajp13Socket.cpp')
-rw-r--r--src/Sockets/Ajp13Socket.cpp404
1 files changed, 404 insertions, 0 deletions
diff --git a/src/Sockets/Ajp13Socket.cpp b/src/Sockets/Ajp13Socket.cpp
new file mode 100644
index 0000000..57755f0
--- /dev/null
+++ b/src/Sockets/Ajp13Socket.cpp
@@ -0,0 +1,404 @@
+/**
+ ** \file Ajp13Socket.cpp
+ ** \date 2007-10-05
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2007 Anders Hedstrom
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifdef _MSC_VER
+#pragma warning(disable:4786)
+#endif
+#include "Ajp13Socket.h"
+#include "ajp13.h"
+#include "HttpRequest.h"
+#include "HttpResponse.h"
+#include "IFile.h"
+#include "Utility.h"
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+#ifdef _DEBUG
+#define DEB(x) x
+#else
+#define DEB(x)
+#endif
+
+
+// --------------------------------------------------------------------------------------
+Ajp13Socket::Ajp13Socket(ISocketHandler& h) : AjpBaseSocket(h)
+, m_body_size_left(0)
+, m_res_file(NULL)
+{
+}
+
+
+// --------------------------------------------------------------------------------------
+void Ajp13Socket::OnHeader( short id, short len )
+{
+ if (id != 0x1234)
+ {
+ fprintf(stderr, "ABORT: bad packet id: %x\n", id);
+ SetCloseAndDelete();
+ }
+ else
+ {
+ DEB(fprintf(stderr, "Packet size: %d bytes\n", len);)
+ }
+}
+
+
+// --------------------------------------------------------------------------------------
+void Ajp13Socket::ReceiveBody(const char *buf, size_t sz)
+{
+ if (sz - 2 > m_body_size_left)
+ {
+ fprintf(stderr, "More body data received than expected\n");
+ SetCloseAndDelete();
+ return;
+ }
+
+ m_req.Write( buf + 2, sz - 2 );
+ m_body_size_left -= sz - 2;
+
+ // request more body data
+ if (m_body_size_left)
+ {
+ int ptr = 4;
+ char msg[100];
+ msg[0] = 'A';
+ msg[1] = 'B';
+
+// reply codes
+// 0x3 Send Body Chunk
+// 0x4 Send Headers
+// 0x5 End Response
+// 0x6 Get Body Chunk <------
+// 0x9 CPong Reply
+
+ put_byte(msg, ptr, 0x06); // GET_BODY_CHUNK;
+ put_integer(msg, ptr, 1000); // request 1000 bytes
+
+ short len = htons( ptr - 4 );
+ memcpy( msg + 2, &len, 2 );
+
+ SendBuf( msg, ptr );
+ return;
+ }
+
+ // Close
+ m_req.CloseBody();
+
+ // no more body data left to read - execute
+ Execute();
+
+}
+
+
+// --------------------------------------------------------------------------------------
+void Ajp13Socket::ReceiveForwardRequest( const char *buf, size_t sz )
+{
+ //
+ int ptr = 0;
+
+ get_byte(buf, ptr); // skip first byte: prefix_code
+ unsigned char method = get_byte(buf, ptr);
+ std::string protocol = get_string(buf, ptr);
+ std::string req_uri = get_string(buf, ptr);
+ std::string remote_addr = get_string(buf, ptr);
+ std::string remote_host = get_string(buf, ptr);
+ std::string server_name = get_string(buf, ptr);
+ short server_port = get_integer(buf, ptr);
+ bool is_ssl = get_boolean(buf, ptr);
+
+ std::string method_str = Utility::l2string( method );
+ std::map<int, std::string>::const_iterator it = Init.Method.find( method );
+ if (it != Init.Method.end())
+ {
+ method_str = it -> second;
+ }
+ m_req.SetHttpMethod( method_str );
+ m_req.SetHttpVersion( protocol );
+ m_req.SetUri( req_uri );
+ m_req.SetRemoteAddr( remote_addr );
+ m_req.SetRemoteHost( remote_host );
+ m_req.SetServerName( server_name );
+ m_req.SetServerPort( server_port );
+ m_req.SetIsSsl( is_ssl );
+
+ // Get Headers
+ short num_headers = get_integer(buf, ptr);
+ for (int i = 0; i < num_headers; i++)
+ {
+ std::string key;
+ switch ( (unsigned char)buf[ptr]) // 0xa0
+ {
+ case 0xa0:
+ {
+ unsigned short x = (unsigned short)get_integer(buf, ptr);
+ std::map<int, std::string>::const_iterator it;
+ if ( (it = Init.Header.find(x)) != Init.Header.end())
+ {
+ key = it -> second;
+ }
+ else
+ {
+ fprintf(stderr, "Unknown header key value: %x\n", x);
+ SetCloseAndDelete();
+ }
+ }
+ break;
+
+ default: // string
+ key = get_string(buf, ptr);
+ }
+ if (Utility::ToLower(key) == "cookie" || Utility::ToLower(key) == "cookie2")
+ m_req.AddCookie(get_string(buf, ptr));
+ else
+ m_req.SetHeader(key, get_string(buf, ptr));
+ } // for
+
+ // size left to read from web server
+ m_body_size_left = m_req.ContentLength();
+
+ // Get Attributes
+ while ( (unsigned char)buf[ptr] != 0xff)
+ {
+ std::string key;
+ unsigned char code = buf[ptr++];
+ switch ( code)
+ {
+ case 10: // req_attribute, attribute name follow
+ key = get_string(buf, ptr);
+ break;
+ default:
+ {
+ std::map<int, std::string>::const_iterator it = Init.Attribute.find( code );
+ if (it != Init.Attribute.end())
+ {
+ key = it -> second;
+ }
+ else
+ {
+ fprintf(stderr, "Unknown attribute key: 0x%02x\n", buf[ptr]);
+ SetCloseAndDelete();
+ }
+ }
+ }
+ m_req.SetAttribute(key, get_string(buf, ptr));
+ } // while
+
+ // execute at once if no body data
+ if (!m_body_size_left)
+ {
+ Execute();
+ }
+ else
+ {
+ // open temporary file for body data
+ m_req.InitBody( m_body_size_left );
+ }
+}
+
+
+// --------------------------------------------------------------------------------------
+void Ajp13Socket::ReceiveShutdown( const char *buf, size_t sz )
+{
+}
+
+
+// --------------------------------------------------------------------------------------
+void Ajp13Socket::ReceivePing( const char *buf, size_t sz )
+{
+}
+
+
+// --------------------------------------------------------------------------------------
+void Ajp13Socket::ReceiveCPing( const char *buf, size_t sz )
+{
+}
+
+
+// --------------------------------------------------------------------------------------
+void Ajp13Socket::Execute()
+{
+ // parse form data / query_string and cookie header if available
+ m_req.ParseBody();
+
+ // prepare page
+ OnExec( m_req );
+
+}
+
+
+// --------------------------------------------------------------------------------------
+void Ajp13Socket::Respond(const HttpResponse& res)
+{
+ char msg[8192];
+ msg[0] = 'A';
+ msg[1] = 'B';
+
+// reply codes
+// 0x3 Send Body Chunk
+// 0x4 Send Headers
+// 0x5 End Response
+// 0x6 Get Body Chunk
+// 0x9 CPong Reply
+
+ // check content length
+ if (!res.ContentLength() && res.GetFile().size())
+ {
+// res.SetContentLength( res.GetFile().size() );
+ }
+
+ // Send Headers
+ {
+ int ptr = 4;
+ put_byte(msg, ptr, 0x04); // send headers
+ put_integer(msg, ptr, res.HttpStatusCode() );
+ put_string(msg, ptr, res.HttpStatusMsg() );
+ put_integer(msg, ptr, (short)res.Headers().size() );
+ for (std::map<std::string, std::string>::const_iterator it = res.Headers().begin(); it != res.Headers().end(); ++it)
+ {
+ std::map<std::string, int>::const_iterator it2 = Init.ResponseHeader.find( it -> first );
+ if (it2 != Init.ResponseHeader.end())
+ {
+ put_integer(msg, ptr, it2 -> second);
+ }
+ else
+ {
+ put_string(msg, ptr, it -> first);
+ }
+ put_string(msg, ptr, it -> second);
+ }
+ std::list<std::string> vec = res.CookieNames();
+ {
+ for (std::list<std::string>::iterator it = vec.begin(); it != vec.end(); it++)
+ {
+ std::map<std::string, int>::const_iterator it2 = Init.ResponseHeader.find( "set-cookie" );
+ if (it2 != Init.ResponseHeader.end())
+ {
+ put_integer(msg, ptr, it2 -> second);
+ }
+ else
+ {
+ put_string(msg, ptr, "set-cookie");
+ }
+ put_string(msg, ptr, res.Cookie(*it) );
+ }
+ }
+
+ short len = htons( ptr - 4 );
+ memcpy( msg + 2, &len, 2 );
+
+ SendBuf( msg, ptr );
+ }
+ m_res_file = &res.GetFile();
+ // Send Body Chunk
+ OnTransferLimit();
+}
+
+
+// --------------------------------------------------------------------------------------
+void Ajp13Socket::OnTransferLimit()
+{
+ char msg[8192];
+ msg[0] = 'A';
+ msg[1] = 'B';
+
+ // Send Body Chunk
+ size_t n = m_res_file -> fread(msg + 7, 1, 8100);
+ while (n > 0)
+ {
+ int ptr = 4;
+ put_byte(msg, ptr, 0x03); // send body chunk
+ put_integer(msg, ptr, (short)n);
+ ptr += (int)n;
+
+ short len = htons( ptr - 4 );
+ memcpy( msg + 2, &len, 2 );
+
+ SendBuf( msg, ptr );
+ if (GetOutputLength() > 1)
+ {
+ SetTransferLimit( 1 );
+ break;
+ }
+
+ //
+ n = m_res_file -> fread(msg + 7, 1, 8100);
+ }
+ if (!GetOutputLength()) // all body data sent and no data in output buffer - send end response
+ {
+ // End Response
+ int ptr = 4;
+ put_byte(msg, ptr, 0x05); // end response
+ put_boolean(msg, ptr, false); // reuse
+ /*
+ don't reuse
+ - but with m_req.Reset() and res.Reset() it should be possible
+ - also reset any AjpBaseSocket/Ajp13Socket specific states
+ */
+
+ short len = htons( ptr - 4 );
+ memcpy( msg + 2, &len, 2 );
+
+ SendBuf( msg, ptr );
+ }
+}
+
+
+// --------------------------------------------------------------------------------------
+void Ajp13Socket::OnPacket( const char *buf, size_t sz )
+{
+ DEB(fprintf(stderr, "OnPacket: %d bytes, code 0x%02x %02x %02x %02x\n", sz, *buf, buf[1], buf[2], buf[3]);)
+
+ // check body size left to read, if non-zero packet is body data
+ if (m_body_size_left) // must be a body packet
+ {
+ ReceiveBody(buf, sz);
+ return;
+ }
+ switch (*buf)
+ {
+ case 0x2: // Forward Request
+ ReceiveForwardRequest(buf, sz);
+ break;
+ case 0x7: // Shutdown
+ ReceiveShutdown(buf, sz);
+ break;
+ case 0x8: // Ping
+ ReceivePing(buf, sz);
+ break;
+ case 0xa: // CPing
+ ReceiveCPing(buf, sz);
+ break;
+ default:
+ fprintf(stderr, "Unknown packet type: 0x%02x\n", *buf);
+ SetCloseAndDelete();
+ }
+
+}
+
+
+#ifdef SOCKETS_NAMESPACE
+} // namespace SOCKETS_NAMESPACE {
+#endif
+
+