You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

578 lines
13 KiB
C++

//#include "../lib_base.h"
#include "http.h"
#ifndef FAIL
#define OK 0
#define FAIL __LINE__
#define DebugOutLn //
#endif
#define NETBLOCKSIZE 8192
#define FTP_TIMEOUT 2000
#define RETRY_CNT 5
int CHttpClient::ReadData(int handle, char * buf, int bufsize)
{
if( buf ){
// netbuf leeren
CNetPacket* NETbuf;
while( NETbuf=m_netbuf.GetFront()){
// wir haben einen netbuffer übrig den wir loswerden müssen
// wenn bufsize < netbuffer size,
if( bufsize < NETbuf->len ){
memcpy(buf, NETbuf->buf, bufsize );
NETbuf->len -= bufsize;
NETbuf->buf += bufsize;
m_netbuf.PutFront(NETbuf);
return bufsize; //-------> exit buffer komplett angefüllt, netbuf partiell weiter
}
else if( bufsize == NETbuf->len ){
memcpy(buf, NETbuf->buf, bufsize );
delete NETbuf;
return bufsize; //--------> exit buffer komplett angefüllt, netbuf gelöscht
}
else {
// bufsize > netbufsize
// wir kopieren kompletten Inhalt
int rc = NETbuf->len;
memcpy(buf, NETbuf->buf, NETbuf->len );
delete NETbuf;
return rc; //--------> exit buffer partiell angefüllt, netbuf gelöscht
}
}
}
struct sockaddr FArc;
#ifdef _UNIX
size_t fsize = sizeof(FArc);
#else
int fsize = sizeof(FArc);
#endif
if( m_pending)
{
CNetPacket* NETbuf = m_pending;
// wir haben einen netbuffer übrig den wir loswerden müssen
// wenn bufsize < netbuffer size,
if( bufsize < NETbuf->len ){
memcpy(buf, NETbuf->buf, bufsize );
NETbuf->len -= bufsize;
NETbuf->buf += bufsize;
return bufsize; //-------> exit buffer komplett angefüllt, netbuf partiell weiter
}
else if( bufsize == NETbuf->len ){
memcpy(buf, NETbuf->buf, bufsize );
delete NETbuf;
m_pending = 0;
return bufsize; //--------> exit buffer komplett angefüllt, netbuf gelöscht
}
else {
// bufsize > netbufsize
// wir kopieren kompletten Inhalt
int rc = NETbuf->len;
memcpy(buf, NETbuf->buf, NETbuf->len );
delete m_pending;
m_pending = 0;
return rc; //--------> exit buffer partiell angefüllt, netbuf gelöscht
}
}
int rc = recvfrom( handle,
buf,
bufsize,
0,
&FArc,
&fsize);
return rc;
}
int CHttpClient::ReadLine(int handle, char * buf, int bufsize)
{
if( buf ){
// netbuf leeren
CNetPacket* NETbuf;
while( NETbuf=m_netbuf.GetFront()){
// wir haben einen netbuffer übrig den wir loswerden müssen
// wenn bufsize < netbuffer size,
if( bufsize < NETbuf->len ){
memcpy(buf, NETbuf->buf, bufsize );
NETbuf->len -= bufsize;
NETbuf->buf += bufsize;
m_netbuf.PutFront(NETbuf);
return bufsize; //-------> exit buffer komplett angefüllt, netbuf partiell weiter
}
else if( bufsize == NETbuf->len ){
memcpy(buf, NETbuf->buf, bufsize );
delete NETbuf;
return bufsize; //--------> exit buffer komplett angefüllt, netbuf gelöscht
}
else {
// bufsize > netbufsize
// wir kopieren kompletten Inhalt
int rc = NETbuf->len;
memcpy(buf, NETbuf->buf, NETbuf->len );
delete NETbuf;
return rc; //--------> exit buffer partiell angefüllt, netbuf gelöscht
}
}
}
struct sockaddr FArc;
#ifdef _UNIX
size_t fsize = sizeof(FArc);
#else
int fsize = sizeof(FArc);
#endif
// gelesen wird in netbuffer,
// es wird in den m_pending netbuffer gelesen bis eine \r\n kombination auftritt
// dann wird der m_pending in die queue gestellt und ein neuer m_pending aufgemacht
if( m_pending == NULL ){
// neuen netblock anlegen
m_pending = new CNetPacket(NETBLOCKSIZE);
memset(m_pending->buf,0,NETBLOCKSIZE);
m_pending->len = 0; // leer
}
memset(buf,0,bufsize);
int rc = recvfrom( handle,
m_pending->buf + m_pending->len,
NETBLOCKSIZE - m_pending->len,
0,
&FArc,
&fsize);
if( rc > 0 ){
// Test auf \r\n Kombination
char *msgEnd;
m_pending->len += rc;
/*
* Solange Sequenzen im Datenstrom
*/
while( m_pending != NULL &&
NULL != (msgEnd=strstr( m_pending->buf,"\r\n"))
)
{
/*
* Msglänge bis zur Sequenz \r\n
* Dise Msg ins m_netbuf array zur Abholung
* Verbleibende msg zurück als m_pending
*/
int msg_len = (msgEnd-m_pending->buf)+2;
/*
* Finished paket erzeugen und auf die queue
* Die Queue beinhaltet nur komplette Sätze
*/
CNetPacket *finished = new CNetPacket(msg_len);
memcpy( finished->buf, m_pending->buf, msg_len);
m_netbuf.PutBack(finished);
/*
* Partiell geladene Netpakete als neues m_pending
* zurückstellen
*/
int msgsize_left = m_pending->len - msg_len;
if( msgsize_left > 0 ){
finished = new CNetPacket(NETBLOCKSIZE);
memcpy(finished->buf,m_pending->buf+msg_len,msgsize_left);
finished->len = msgsize_left;
delete m_pending;
m_pending = finished;
}
else
{
// Keine weiteren daten m_pending löschen
delete m_pending;
m_pending = 0;
}
}
return ReadLine(handle, buf, bufsize);
}
////DebugOutLn("Read(%x)rc=%d: %s",handle,rc,buf);
return rc;
}
int CHttpClient::Read(int handle, char * buf, int bufsize)
{
struct sockaddr FArc;
#ifdef _UNIX
size_t fsize = sizeof(FArc);
#else
int fsize = sizeof(FArc);
#endif
memset(buf,0,bufsize);
int rc = recvfrom( handle, buf, bufsize,0,&FArc, &fsize);
////DebugOutLn("Read(%x)rc=%d: %s",handle,rc,buf);
return rc;
}
CHttpClient::~CHttpClient()
{
Server.CloseServer();
HttpTransfer.CloseServer();
status = CONNECT_TO_HTTP;
DestroyBuffer();
}
void CHttpClient::DestroyBuffer()
{
delete m_pending;
m_pending = 0;
CNetPacket *P;
while( NULL!=(P=m_netbuf.Get()) ){
delete P;
}
}
CHttpClient::CHttpClient(char *http_server,char *http_user,char *http_pass)
{
m_pending = 0;
m_filesize = 0;
if( http_server == 0 )
{
DebugOutLn("CHttpClient():Invalid Param http_server");
m_last_error = FAIL;
return;
}
status = CONNECT_TO_HTTP;
Server.StartUpAsClient( http_server, 80, false );
if( Server.GetLastError()){
DebugOutLn("CHttpClient():Connect to server %s, port 80 failed",http_server);
m_last_error = Server.GetLastError();
return;
}
m_ServerName = CNetPacket(http_server);
DebugOutLn("Connected to %s",http_server);
#if 0
DebugOutLn("Waiting for answer");
/************************************************************************/
/* Dieser Status ist die erste Kontaktaufnahme mit dem FTP Server,
hier wird sich zeigen ob die IP Adresse und der Port stimmt.*/
/************************************************************************/
PrepareSelect(Server.GetSockHandle());
int rc;
bool http_active = true;
char buf[NETBLOCKSIZE];
int oldstatus = status;
int retry = RETRY_CNT;
while(http_active ){
//Retrycount rücksetzen
if( oldstatus != status )
{
retry = RETRY_CNT;
oldstatus = status;
}
switch( status )
{
case CONNECT_TO_HTTP:
rc = WaitFD( FTP_TIMEOUT ); // wait select mit timeout in millisekunden
if( rc != 0 ){
// Antwort
int bytes = ReadLine(Server.GetSockHandle(), buf, sizeof(buf));
if( bytes < 0 )
{
DebugOutLn("CHttpClient: Timeout connecting to server");
m_last_error = FAIL;
return;
}
status = CONNECTED;
http_active = false; // alles ok
}
break;
}
}
#endif
status = CONNECTED;
m_last_error = 0;
return;
}
void CHttpClient::PrepareSelect( int fd1)
{
FD_ZERO(&fd);
FD_SET( fd1, &fd );
}
void CHttpClient::PrepareSelect( int fd1, int fd2)
{
FD_ZERO(&fd);
FD_SET( fd1, &fd );
FD_SET( fd2, &fd );
}
void CHttpClient::PrepareSelect( int fd1, int fd2, int fd3)
{
FD_ZERO(&fd);
FD_SET( fd1, &fd );
FD_SET( fd2, &fd );
FD_SET( fd3, &fd );
}
int CHttpClient::WaitFD(int timeoutms)
{
/*
* Select BLOCKED
*/
struct timeval timeout;
timeout.tv_sec = timeoutms/1000;
timeout.tv_usec = (timeoutms%1000)*1000;
int rc = select(FD_SETSIZE, &fd, 0, 0, &timeout );
if(rc<1){
PrepareSelect(Server.GetSockHandle());
};
return rc;
}
int CHttpClient::Get(char * file, FILE * fd)
{
if( !(status == CONNECTED) )
{
DebugOutLn("CHttpClient::Get() - not connected");
m_last_error = FAIL;
DestroyBuffer();
return m_last_error;
}
status = CONNECTED;
m_last_error = 0;
return m_last_error;
}
int CHttpClient::Close()
{
HttpTransfer.CloseServer();
status = CONNECT_TO_HTTP;
DestroyBuffer();
return 0;
}
int CHttpClient::Open(char * filename)
{
if( status != CONNECTED )
{
// Reconnect
if( m_ServerName.len == 0 )
{
DebugOutLn("CHttpClient::Open(%s): failed, no servername",filename);
DestroyBuffer();
m_last_error = FAIL;
return m_last_error;
}
Server.StartUpAsClient( m_ServerName.buf, 80, false );
if( Server.GetLastError()){
DebugOutLn("CHttpClient():Connect to server %s, port 80 failed",m_ServerName.buf);
m_last_error = Server.GetLastError();
DestroyBuffer();
return m_last_error;
}
status = CONNECTED;
}
DestroyBuffer();
int rc;
bool http_active = true;
int ipadr,port;
char buf[NETBLOCKSIZE];
PrepareSelect(Server.GetSockHandle());
m_ETag.len = 0;
m_filesize = 0;
status = CONNECTED_SENDING_GET;
int oldstatus = status;
int retry = RETRY_CNT;
while(http_active){
//Retrycount rücksetzen
if( oldstatus != status )
{
retry = RETRY_CNT;
oldstatus = status;
}
switch( status )
{
/************************************************************************/
/* Senden PASV */
/************************************************************************/
case CONNECTED_SENDING_GET:
rc = sprintf(buf,"GET %s HTTP/1.1\r\n"
"Host: %s\r\n"
"User-Agent: GamePlayer/1.0 (Windows32)\r\n"
"Accept: */*\r\n"
"\r\n"
, filename,m_ServerName.buf);
Server.Write(buf,rc);
status = WAIT_200;
break;
case WAIT_200:
rc = WaitFD(FTP_TIMEOUT);
if( rc < 1 ){
if( --retry == 0 )
{
DebugOutLn("CHttpClient: timeout waiting 200");
m_last_error = FAIL;
DestroyBuffer();
return m_last_error;
}
}
else{
ReadLine(Server.GetSockHandle(),0,0);
CNetPacket * NP;
while( NULL != (NP = m_netbuf.GetFront())){
if( strncmp(NP->buf,"HTTP/1.1 200",12)== 0 ){
m_last_error = OK;
}
if( strncmp(NP->buf,"HTTP/1.1 4",10)== 0 ){
DebugOutLn("CHttpClient::Open(%s): Error: %s",filename,NP->buf);
}
if( strncmp(NP->buf,"connection: close",17)== 0 ){
status=CONNECT_TO_HTTP;
}
if( strncmp(NP->buf,"Last-Modified: ",15)== 0 ){
m_LastModified = CNetPacket(NP->buf+16);
}
if( strncmp(NP->buf,"ETag:",5)==0)
{
// Checksummen string für dieses File
// Könnte eine MD5 sein,muss aber nicht,
// in jeden Fall reicht ein Vergleich zwischen
// alten und aktuellen eTag
char eTag[256];
char *A,*B;
A = strchr(NP->buf, '"');
if( A ){
A++;
B = strrchr(NP->buf,'"');
if( B > A ){
int idx = 0;
while( A < B )
eTag[idx++] = *A++;
eTag[idx]=0;
m_ETag = CNetPacket(eTag);
}
}
}
if( strncmp(NP->buf,"Content-Length: ",16)==0){
m_filesize = atoi(NP->buf+16);
}
if( strcmp(NP->buf,"\r\n")==0)
{
// Ende Header //----------------> unbedingt break, weil sonst
// ------------------------------> m_netbuf Geleert ist !
// ------------------------------> im netbuf ist aber der erste
// ------------------------------> teil vom content!!!!
http_active = false;
if( m_filesize > 0 )
status = WAIT_TRANSFER_FLUSHED;
break; //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
}
//MsgBox(NP->buf);
DebugOutLn(NP->buf);
delete NP;
}
if( --retry == 0 )
{
DebugOutLn("CHttpClient::Open(%s): timeout waiting 200",filename);
m_last_error = FAIL;
DestroyBuffer();
return m_last_error;
}
}
break;
}//switch status
}//while fptacktive
//
if( m_filesize > 0 && m_ETag.len > 0 ){
m_last_error = 0;
}
else{
DebugOutLn("CHttpClient::Open(%s): failed",filename);
m_last_error = FAIL;
}
return m_last_error;
}
/************/
/* LIST */
/************/
int CHttpClient::List(char * dir, char *listbuf, int listbufsize)
{
// dir muss den filenamen enthalten
if( dir == 0 )
return FAIL;
// kein filename (im gegeseatz zum ftp/list) sollte den Root file bringen
if( *dir == 0 )
strcpy(dir,"/");
status = CONNECT_TO_HTTP;
int rc = Open(dir);
if( rc == OK ){
char buf[256];
int len = sprintf(buf,"%s %s %d",m_LastModified.buf,dir,m_filesize);
if( len < listbufsize )
strcpy(listbuf,buf);
}
status = CONNECT_TO_HTTP;
return rc;
}
int CHttpClient::ReadHttp(char * buf, int size)
{
if( status != WAIT_TRANSFER_FLUSHED )
return -1; // ende der übertragung
if( Server.GetSockHandle() > 0 )
{
memset(buf,0,size);
int bytes = ReadData( Server.GetSockHandle(), buf, size );//Read(Server.GetSockHandle(), buf, size);
if( bytes > 0 ){
return bytes;
}
else if( bytes == 0 )
{
HttpTransfer.CloseServer();
return -1;
}
}
return 0;
}