//#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; }