/** ** \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::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::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::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::const_iterator it = res.Headers().begin(); it != res.Headers().end(); ++it) { std::map::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 vec = res.CookieNames(); { for (std::list::iterator it = vec.begin(); it != vec.end(); it++) { std::map::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