Files
resiprocate/apps/clicktocall/HttpConnection.cxx
2015-07-10 11:54:21 +02:00

422 lines
11 KiB
C++

#include "rutil/ResipAssert.h"
#include <rutil/Data.hxx>
#include <rutil/Socket.hxx>
#include <resip/stack/Symbols.hxx>
#include <rutil/TransportType.hxx>
#include <rutil/Logger.hxx>
#include <resip/stack/Tuple.hxx>
#include <rutil/DnsUtil.hxx>
#include <rutil/ParseBuffer.hxx>
#include "Version.hxx"
#include "AppSubsystem.hxx"
#include "Server.hxx"
#include "HttpBase.hxx"
#include "HttpConnection.hxx"
using namespace clicktocall;
using namespace resip;
using namespace std;
#define RESIPROCATE_SUBSYSTEM AppSubsystem::CLICKTOCALL
int HttpConnection::nextPageNumber=1;
HttpConnection::HttpConnection( HttpBase& base, resip::Socket pSock ):
mHttpBase( base ),
mPageNumber(nextPageNumber++),
mSock(pSock),
mParsedRequest(false)
{
resip_assert( mSock > 0 );
}
HttpConnection::~HttpConnection()
{
resip_assert( mSock > 0 );
#ifdef WIN32
closesocket(mSock); mSock=0;
#else
close(mSock); mSock=0;
#endif
}
void
HttpConnection::buildFdSet(FdSet& fdset)
{
if ( !mTxBuffer.empty() )
{
fdset.setWrite(mSock);
}
fdset.setRead(mSock);
}
bool
HttpConnection::process(FdSet& fdset)
{
if ( fdset.hasException(mSock) )
{
int errNum = 0;
int errNumSize = sizeof(errNum);
getsockopt(mSock,SOL_SOCKET,SO_ERROR,(char *)&errNum,(socklen_t *)&errNumSize);
InfoLog (<< "Exception reading from socket "
<< (int)mSock << " code: " << errNum << "; closing connection");
return false;
}
if ( fdset.readyToRead( mSock ) )
{
bool ok = processSomeReads();
if ( !ok )
{
return false;
}
}
if ( (!mTxBuffer.empty()) && fdset.readyToWrite( mSock ) )
{
bool ok = processSomeWrites();
if ( !ok )
{
return false;
}
}
return true;
}
void
HttpConnection::setPage(const Data& pPage,int response,const Mime& pType)
{
Data page(pPage);
switch (response)
{
case 401:
{
mTxBuffer += "HTTP/1.0 401 Unauthorized"; mTxBuffer += Symbols::CRLF;
page = ("<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">"
"<html><head>"
"<title>401 Unauthorized</title>"
"</head><body>"
"<h1>Unauthorized</h1>"
"</body></html>" );
}
break;
case 404:
{
mTxBuffer += "HTTP/1.0 404 Not Found"; mTxBuffer += Symbols::CRLF;
page = ("<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">"
"<html><head>"
"<title>404 Not Found</title>"
"</head><body>"
"<h1>Unauthorized</h1>"
"</body></html>" );
}
break;
case 301:
{
mTxBuffer += "HTTP/1.0 301 Moved Permanently"; mTxBuffer += Symbols::CRLF;
mTxBuffer += "Location: http:/index.html"; mTxBuffer += Symbols::CRLF;
page = ("<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">"
"<html><head>"
"<title>301 Moved Permanently</title>"
"</head><body>"
"<h1>Moved</h1>"
"</body></html>" );
}
break;
case 200:
{
mTxBuffer += "HTTP/1.0 200 OK" ; mTxBuffer += Symbols::CRLF;
}
break;
default:
{
resip_assert(0);
Data resp;
{
DataStream s(resp);
s << response;
s.flush();
}
mTxBuffer += "HTTP/1.0 ";
mTxBuffer += resp;
mTxBuffer += "OK" ; mTxBuffer += Symbols::CRLF;
}
break;
}
Data len;
{
DataStream s(len);
s << page.size();
s.flush();
}
mTxBuffer += "WWW-Authenticate: Basic realm=\"";
if ( mHttpBase.mRealm.empty() )
{
mTxBuffer += resip::DnsUtil::getLocalHostName();
}
else
{
mTxBuffer += mHttpBase.mRealm;
}
mTxBuffer += "\" ";
mTxBuffer += Symbols::CRLF;
mTxBuffer += "Server: clicktocall " ;
mTxBuffer += Data(CLICKTOCALL_VERSION_STRING);
mTxBuffer += Symbols::CRLF;
mTxBuffer += "Mime-version: 1.0 " ; mTxBuffer += Symbols::CRLF;
mTxBuffer += "Pragma: no-cache " ; mTxBuffer += Symbols::CRLF;
mTxBuffer += "Content-Length: "; mTxBuffer += len; mTxBuffer += Symbols::CRLF;
mTxBuffer += "Content-Type: " ;
mTxBuffer += pType.type() ;
mTxBuffer +="/" ;
mTxBuffer += pType.subType() ; mTxBuffer += Symbols::CRLF;
mTxBuffer += Symbols::CRLF;
mTxBuffer += page;
}
bool
HttpConnection::processSomeReads()
{
const int bufSize = 8000;
char buf[bufSize];
#if defined(WIN32)
int bytesRead = ::recv(mSock, buf, bufSize, 0);
#else
int bytesRead = ::read(mSock, buf, bufSize);
#endif
if (bytesRead == INVALID_SOCKET)
{
int e = getErrno();
switch (e)
{
case EAGAIN:
InfoLog (<< "No data ready to read");
return true;
case EINTR:
InfoLog (<< "The call was interrupted by a signal before any data was read.");
break;
case EIO:
InfoLog (<< "I/O error");
break;
case EBADF:
InfoLog (<< "fd is not a valid file descriptor or is not open for reading.");
break;
case EINVAL:
InfoLog (<< "fd is attached to an object which is unsuitable for reading.");
break;
case EFAULT:
InfoLog (<< "buf is outside your accessible address space.");
break;
default:
InfoLog (<< "Some other error");
break;
}
InfoLog (<< "Failed read on " << (int)mSock << " " << strerror(e));
return false;
}
else if (bytesRead == 0)
{
InfoLog (<< "Connection closed by remote " );
return false;
}
//DebugLog (<< "HttpConnection::processSomeReads() "
// << " read=" << bytesRead);
mRxBuffer += Data( buf, bytesRead );
tryParse();
return true;
}
void
HttpConnection::tryParse()
{
//DebugLog (<< "parse " << mRxBuffer );
ParseBuffer pb(mRxBuffer);
pb.skipToChar(Symbols::SPACE[0]);
const char* start = pb.skipWhitespace();
pb.skipToChar(Symbols::SPACE[0]);
if (pb.eof())
{
// parse failed - just return
return;
}
Data uri;
pb.data( uri, start );
DebugLog (<< "parse found URI " << uri );
mParsedRequest = true;
Data user;
Data password;
try
{
pb.skipToChars( "Authorization" );
if ( !pb.eof() )
{
if ( pb.eof() ) DebugLog( << "Did not find Authorization header" );
pb.skipToChars( "Basic" ); pb.skipN(6);
if ( pb.eof() ) DebugLog( << "Did not find Authorization basic " );
pb.skipWhitespace();
if ( pb.eof() ) DebugLog( << "Something weird in Auhtorization header " );
if ( !pb.eof() )
{
const char* a = pb.position();
pb.skipNonWhitespace();
Data buf = pb.data(a);
DebugLog (<< "parse found basic base64 auth data of " << buf );
Data auth = buf.base64decode();
//DebugLog (<< "parse found basic auth data of " << auth );
ParseBuffer p(auth);
const char* a1 = p.position();
p.skipToChar(':');
user = p.data(a1);
const char* a2 = p.skipChar(':');
p.skipToEnd();
password = p.data(a2);
//DebugLog (<< "parse found basic auth data with user=" << user
// << " password=" << password );
}
}
}
catch ( ... )
{
ErrLog (<< "Some problem finding Authorization header in HTTP request" );
}
mHttpBase.buildPage(uri,mPageNumber,user,password);
}
bool
HttpConnection::processSomeWrites()
{
if ( mTxBuffer.empty() )
{
return true;
}
//DebugLog (<< "Writing " << mTxBuffer );
#if defined(WIN32)
int bytesWritten = ::send( mSock, mTxBuffer.data(), (int)mTxBuffer.size(), 0);
#else
int bytesWritten = ::write(mSock, mTxBuffer.data(), mTxBuffer.size() );
#endif
if (bytesWritten == INVALID_SOCKET)
{
int e = getErrno();
InfoLog (<< "HttpConnection failed write on " << mSock << " " << strerror(e));
return false;
}
if (bytesWritten == (int)mTxBuffer.size() )
{
DebugLog (<< "Wrote it all" );
mTxBuffer = Data::Empty;
return false; // return false causes connection to close and clean up
}
else
{
Data rest = mTxBuffer.substr(bytesWritten);
mTxBuffer = rest;
DebugLog( << "Wrote " << bytesWritten << " bytes - still need to do " << mTxBuffer );
}
return true;
}
/* ====================================================================
* The Vovida Software License, Version 1.0
*
* Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The names "VOCAL", "Vovida Open Communication Application Library",
* and "Vovida Open Communication Application Library (VOCAL)" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact vocal@vovida.org.
*
* 4. Products derived from this software may not be called "VOCAL", nor
* may "VOCAL" appear in their name, without prior written
* permission of Vovida Networks, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
* NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
* NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
* IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* ====================================================================
*
* This software consists of voluntary contributions made by Vovida
* Networks, Inc. and many individuals on behalf of Vovida Networks,
* Inc. For more information on Vovida Networks, Inc., please see
* <http://www.vovida.org/>.
*
*/