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++
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;
|
|
}
|