mirror of
https://github.com/resiprocate/resiprocate.git
synced 2026-01-12 00:05:02 +08:00
422 lines
11 KiB
C++
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/>.
|
|
*
|
|
*/
|