Сел изобретать велосипед... :))
Вот понадобилась такая простенькая вещь:
1. Платформа — Windows.
2. Класс для получения информации по HTTP
3. Поддержка GET/POST
4. Поддержка HTTP-прокси и авторизации через прокси
5. Без использования высокоуровневых оберток типа CString и пр.
Вот думаю: изобретать или есть готовый на С/С++? В принципе, изобретено уже процентов 60, но терзают смутные сомнения, что все уже украдено до нас.
Размер — имеет большое значение
To moderator: я намеренно запостил в форум по С/С++, т.к. нужно именно код, на 99% написанный на С/С++.
Что подскажут уважаемые?
10.01.04 16:17: Перенесено из 'C/C++'
Re: Сел изобретать велосипед... :))
Здравствуйте, Flamer, Вы писали:
F>2. Класс для получения информации по HTTP
Посмотри libwww от w3c.
... << RSDN@Home 1.1.0 stable >>
Re: Сел изобретать велосипед... :))
Здравствуйте, Flamer, Вы писали:
F>Вот понадобилась такая простенькая вещь:
F>1. Платформа — Windows.
F>2. Класс для получения информации по HTTP
F>3. Поддержка GET/POST
F>4. Поддержка HTTP-прокси и авторизации через прокси
F>5. Без использования высокоуровневых оберток типа CString и пр.
А чем WinInet плох?
Если нам не помогут, то мы тоже никого не пощадим.
Re[2]: Сел изобретать велосипед... :))
Здравствуйте, IT, Вы писали:
IT>Здравствуйте, Flamer, Вы писали:
F>>Вот понадобилась такая простенькая вещь:
F>>1. Платформа — Windows.
F>>2. Класс для получения информации по HTTP
F>>3. Поддержка GET/POST
F>>4. Поддержка HTTP-прокси и авторизации через прокси
F>>5. Без использования высокоуровневых оберток типа CString и пр.
IT>А чем WinInet плох?
Если синхронный инет — хреново, асинхронный — геморно. Есть у меня класс на асинк-инете, но он написан хреново.. Работает хорошо, но слишком много там исправлений всяких. Если что-то серьезное качать — надо переписывать. Могу сбросить как семпл..
Re[2]: Сел изобретать велосипед... :))
Здравствуйте, IT, Вы писали:
[]
IT>А чем WinInet плох?
Да ничем, в принципе. Просто нужны такае вещи, как таймаут на коннект, на прием, прерывание скачивания в любое время. В общем, этакий коллбэк с UI. Как сказали выше — синхронный WinInet — лажа, асинхронный — гемор. Склонен с этим согласиться...
Да и потом — вполне возможно, что данный классец будет юзаться и под *nix.
З.Ы. В принципе, я уже написал все... Осталось продебажить и собрать ценные замечания. Если вдруг кому интересно — то могу кинуть в "Исходники"
Re[3]: Сел изобретать велосипед... :))
От:
dad
Дата: 10.01.04 11:58
Оценка:
F>З.Ы. В принципе, я уже написал все... Осталось продебажить и собрать ценные замечания. Если вдруг кому интересно — то могу кинуть в "Исходники"
давай. с удовольсвием покритикую
Веру-ю-у! В авиацию, в научную революци-ю-у, в механизацию сельского хозяйства, в космос и невесомость! Веру-ю-у! Ибо это объективно-о! (Шукшин)
Re[3]: Сел изобретать велосипед... :))
От:
ArtDenis
Дата: 10.01.04 13:01
Оценка:
Здравствуйте, Flamer, Вы писали:
F>З.Ы. В принципе, я уже написал все... Осталось продебажить и собрать ценные замечания. Если вдруг кому интересно — то могу кинуть в "Исходники"
Конечно, интересно!
... << RSDN@Home 1.1.0 stable >>
Re: Сел изобретать велосипед... :))
Здравствуйте, Flamer, Вы писали:
F>Вот понадобилась такая простенькая вещь:
По просьбам трудящихся совсем сырые исходники того, что нужно было в первом посте... Никакой оптимизации, код сырой, все писалось за 3 часа на коленке. Сорри за объем кода. Итак:
Оговорюсь, что юзается один сторонний класс CBase64Coder. Его исходник пойдет отдельным постом в форум "Исходники".
1. Сразу про использование. Пока тестировал так (на Билдере):
// функция-коллбэк для ловли момента прерывания закачки
bool __stdcall OnCancel(void * pForm)
{
TForm1* frm = (TForm1*) pForm;
if (!frm)
return true ;
return frm->GetStopped();
}
//---------------------------------------------------------------------------
URLUtils::CHTTPRequest Request;
/*
Request.SetUseProxy(false);
Request.SetProxyPort(8080);
Request.SetProxyAddress("www-proxy.cytanet.com.cy");
Request.SetProxyLogin("login");
Request.SetProxyPassword("pass");
*/
Request.SetCancelCallback(OnCancel,this );
Request.MakeRequest("http://www.rsdn.ru" ,URLUtils::rmGet);
2. Хидер:
//---------------------------------------------------------------------------
#ifndef URLUtilsH
#define URLUtilsH
//---------------------------------------------------------------------------
#include <winsock.h>
//---------------------------------------------------------------------------
#ifdef _DEBUG
#define _OUTPUT(x) OutputDebugString((x));
#else
#define _OUTPUT(x) ((void )0)
#endif
//---------------------------------------------------------------------------
#define HTTPREQ_TIMEOUT 1000
#define HTTPREQ_CANCELLED 1001
#define HTTPREQ_CONNECT_FAILED 1002
#define HTTPREQ_NO_CONNECTION 1004
#define HTTPREQ_INVALID_URL 1005
#define HTTPREQ_WSAINIT_FAILED 1006
#define HTTPREQ_BAD_RESPONSE 1007
//---------------------------------------------------------------------------
namespace URLUtils
{
//---------------------------------------------------------------------------
const int ciMaxPartSize = 255;
const int ciMaxURLLen = 1024;
//---------------------------------------------------------------------------
class CURLCracker
{
private :
char * m_strPrefix;
int m_iPrefixLength;
char * m_strHost;
int m_iHostLength;
int m_iPort;
char * m_strDocumentUrl;
int m_iDocUrlLength;
char * m_strData;
int m_iDataLength;
void InitDefault();
void Clear();
public :
bool CrackURL(const char * url);
const char * GetHost() const { return m_strHost?m_strHost:"" ; };
int GetHostLength() const {return m_iHostLength; };
int GetPort() const { return m_iPort; };
const char * GetDocumentURL() const { return m_strDocumentUrl?m_strDocumentUrl:"/" ; };
int GetDocumentURLLength() const { return m_iDocUrlLength; };
const char * GetData() const { return m_strData?m_strData:"" ; };
int GetDataLength() const { return m_iDataLength; };
const char * GetPrefix() const { return m_strPrefix?m_strPrefix:"" ; };
int GetPrefixLength() const { return m_iPrefixLength; };
public :
CURLCracker();
~CURLCracker();
};
//---------------------------------------------------------------------------
typedef enum
{
rmGet,
rmPost
} CRequestMethod;
//---------------------------------------------------------------------------
class CHTTPHeaders
{
private :
char ** m_strstrHeaders;
int m_iHeadersCount;
void Clear();
public :
int GetCount() const { return m_iHeadersCount; };
const char * GetHeader(int num) const ;
const char * GetHeaderValue(const char * header) const ;
const char * GetHeaderValue(int num) const ;
void SetHeaders(const char * raw,int headers_len);
public :
CHTTPHeaders();
~CHTTPHeaders();
};
//---------------------------------------------------------------------------
typedef bool __stdcall (*COnCancelEvent)(void * UserData);
//---------------------------------------------------------------------------
class CHTTPRequest
{
private :
CHTTPHeaders m_Headers;
bool m_bUseProxy;
char m_strProxyAddress[ciMaxPartSize+1];
int m_iProxyPort;
char m_strProxyLogin[ciMaxPartSize+1];
char m_strProxyPassword[ciMaxPartSize+1];
char m_strWorkBuffer[ciMaxURLLen*4+1];
unsigned int m_iReceiveTimeout;
unsigned int m_iConnectTimeout;
unsigned char * m_strRawResponse;
int m_iRawResponseSize;
unsigned char * m_strBodyPtr;
int m_iBodySize;
int m_iResultCode;
void ReleaseBuffers();
void InitDefault();
bool StartWSA();
void StopWSA();
void FindResultCode();
bool SetResultCode(int ResultCode);
void SendString(SOCKET s, const char * str);
void CloseSocket(SOCKET s);
unsigned int LookupAddress(const char * pcHost);
int ConnectEx(SOCKET s, const struct sockaddr *name,int namelen, long timeout);
bool IsReadible(SOCKET s);
bool ReportError(int ResultCode,SOCKET s = INVALID_SOCKET);
void * m_OnCancelUserData;
COnCancelEvent m_pfnOnCancel;
public :
bool MakeRequest(const char * url, CRequestMethod method);
void SetCancelCallback(COnCancelEvent fn, void * UserData);
int GetResultCode() const { return m_iResultCode; };
const CHTTPHeaders* GetHeaders() const { return &m_Headers; }
const unsigned char * GetRawResponse() const { return m_strRawResponse; };
int GetRawResponseSize() const { return m_iRawResponseSize; };
const unsigned char * GetBody() const { return m_strBodyPtr; };
int GetBodySize() const { return m_iBodySize; };
void SetUseProxy(bool up) { m_bUseProxy = up; };
bool GetUseProxy() {return m_bUseProxy; };
void SetProxyPort(int p) { m_iProxyPort = p; };
int GetProxyPort() { return m_iProxyPort; };
void SetProxyAddress(const char * pa);
const char * GetProxyAddress() const { return m_strProxyAddress; };
void SetProxyLogin(const char * pl);
const char * GetProxyLogin() const { return m_strProxyLogin; };
void SetProxyPassword(const char * pp);
const char * GetProxyPassword() const { return m_strProxyPassword; };
void SetReceiveTimeout(unsigned int rt) { if (rt) m_iReceiveTimeout = rt; };
unsigned int GetReceiveTimeout() { return m_iReceiveTimeout; };
void SetConnectTimeout(unsigned int ct) { if (ct) m_iConnectTimeout = ct; };
unsigned int GetConnectTimeout() { return m_iConnectTimeout; };
public :
CHTTPRequest();
~CHTTPRequest();
};
//---------------------------------------------------------------------------
} // namespace URLUtils
//---------------------------------------------------------------------------
#endif
3. Сырец:
//---------------------------------------------------------------------------
#include "URLUtils.h"
#include "Base64Coder.h"
//---------------------------------------------------------------------------
#include <mem.h>
#include <string.h>
#include <stdio.h>
//---------------------------------------------------------------------------
// CURLCracker
//---------------------------------------------------------------------------
using namespace URLUtils;
//---------------------------------------------------------------------------
CURLCracker::CURLCracker()
{
InitDefault();
}
//---------------------------------------------------------------------------
CURLCracker::~CURLCracker()
{
Clear();
}
//---------------------------------------------------------------------------
void CURLCracker::Clear()
{
if (m_strPrefix)
delete [] m_strPrefix;
if (m_strDocumentUrl)
delete [] m_strDocumentUrl;
if (m_strHost)
delete [] m_strHost;
if (m_strData)
delete [] m_strData;
InitDefault();
}
//---------------------------------------------------------------------------
void CURLCracker::InitDefault()
{
m_strHost = m_strDocumentUrl = m_strData = m_strPrefix = NULL;
m_iHostLength = m_iDocUrlLength = m_iDataLength = m_iPrefixLength = 0;
m_iPort = 80;
}
//---------------------------------------------------------------------------
bool CURLCracker::CrackURL(const char * url)
{
Clear();
if (!url)
return false ;
int url_len = strlen(url);
char * domain_ptr = strstr((char *)url,"://" );
if (!domain_ptr)
return false ;
domain_ptr += 3;
m_iPrefixLength = (domain_ptr-url);
m_strPrefix = new char [m_iPrefixLength+1];
if (!m_strPrefix) { m_iPrefixLength = 0; return false ; }
memcpy(m_strPrefix,url,m_iPrefixLength); m_strPrefix[m_iPrefixLength] = 0;
char * document_ptr = strstr(domain_ptr,"/" );
if (!document_ptr)
{
m_iHostLength = (&(url[url_len]) - domain_ptr);
m_strHost = new char [m_iHostLength + 1];
if (!m_strHost) { m_iHostLength = 0; return false ; }
memcpy(m_strHost,domain_ptr,m_iHostLength); m_strHost[m_iHostLength] = 0;
} // if(!document_ptr)
else
{
m_iHostLength = (document_ptr - domain_ptr);
m_strHost = new char [m_iHostLength+1];
if (!m_strHost){ m_iHostLength=0; return false ; }
memcpy(m_strHost,domain_ptr,m_iHostLength); m_strHost[m_iHostLength] = 0;
m_iDocUrlLength = (&(url[url_len]) - document_ptr);
m_strDocumentUrl = new char [m_iDocUrlLength + 1];
if (!m_strDocumentUrl) { m_iDocUrlLength = 0; return false ; }
memcpy(m_strDocumentUrl,document_ptr,m_iDocUrlLength); m_strDocumentUrl[m_iDocUrlLength] = 0;
} // else
char * data_ptr = m_strDocumentUrl ? strstr(m_strDocumentUrl,"?" ):NULL;
if (data_ptr)
{
m_iDataLength = &(m_strDocumentUrl[m_iDocUrlLength]) - data_ptr - 1;
m_strData = new char [m_iDataLength+1];
if (!m_strData) { m_iDataLength = 0; return false ; }
memcpy(m_strData,data_ptr+1,m_iDataLength); m_strData[m_iDataLength] = 0;
*data_ptr = 0;
} // if(data_ptr)
char * port_ptr = strstr(m_strHost,":" );
if (port_ptr)
{
m_iPort = atoi(port_ptr+1);
*port_ptr = 0;
} //if(port_ptr)
return true ;
}
//---------------------------------------------------------------------------
// CHTTPRequest
//---------------------------------------------------------------------------
CHTTPRequest::CHTTPRequest() :
m_strRawResponse(NULL)
, m_strBodyPtr(NULL)
, m_iBodySize(0)
, m_iRawResponseSize(0)
, m_OnCancelUserData(NULL)
, m_pfnOnCancel(NULL)
{
InitDefault();
}
//---------------------------------------------------------------------------
CHTTPRequest::~CHTTPRequest()
{
ReleaseBuffers();
}
//---------------------------------------------------------------------------
void CHTTPRequest::SetCancelCallback(COnCancelEvent fn, void * UserData)
{
m_pfnOnCancel = fn;
m_OnCancelUserData = UserData;
}
//---------------------------------------------------------------------------
void CHTTPRequest::SetProxyAddress(const char * pa)
{
strncpy(m_strProxyAddress,pa,ciMaxPartSize);
}
//---------------------------------------------------------------------------
void CHTTPRequest::SetProxyLogin(const char * pl)
{
strncpy(m_strProxyLogin,pl,ciMaxPartSize);
}
//---------------------------------------------------------------------------
void CHTTPRequest::SetProxyPassword(const char * pp)
{
strncpy(m_strProxyPassword,pp,ciMaxPartSize);
}
//---------------------------------------------------------------------------
void CHTTPRequest::InitDefault()
{
memset(m_strProxyAddress,0,ciMaxPartSize+1);
memset(m_strProxyLogin,0,ciMaxPartSize+1);
memset(m_strProxyPassword,0,ciMaxPartSize+1);
memset(m_strWorkBuffer,0,ciMaxURLLen*4+1);
m_bUseProxy = false ;
m_iReceiveTimeout = 30;
m_iConnectTimeout = 30;
}
//---------------------------------------------------------------------------
void CHTTPRequest::SendString(SOCKET s, const char * str)
{
send(s,str,strlen(str),0);
}
//---------------------------------------------------------------------------
unsigned int CHTTPRequest::LookupAddress(const char * pcHost)
{ // lookup remote address
unsigned int nRemoteAddr = inet_addr(pcHost);
if (nRemoteAddr == INADDR_NONE) {
hostent* pHE = gethostbyname(pcHost);
if (pHE == 0) return INADDR_NONE;
nRemoteAddr = *((u_long*)pHE->h_addr_list[0]);
}
return nRemoteAddr;
}
//---------------------------------------------------------------------------
bool CHTTPRequest::MakeRequest(const char * url, CRequestMethod method)
{
if (!StartWSA())
return SetResultCode(HTTPREQ_WSAINIT_FAILED);
CURLCracker crackedURL;
if (!crackedURL.CrackURL(url))
return SetResultCode(HTTPREQ_INVALID_URL);
char * Host = (char *) crackedURL.GetHost();
int Port = 80;
if (m_bUseProxy)
{
Host = m_strProxyAddress;
Port = m_iProxyPort;
} // if(m_bUseProxy)
// get_request parameters:
// 1) %s - document URL
// 2) %s - host
// 3) %s - additional headers
const char * get_request="GET %s%s%s HTTP/1.0\r\nAccept: */*\r\nUser-Agent: Mozilla/4.0\r\n"
"Host: %s\r\nConnection: close\r\nPragma: no-cache\r\n%s\r\n" ;
// post_request parameters:
// 1) %s - document URL
// 2) %s - host
// 3) %u - content length
// 4) %s - additional headers
// 5) %s - post data
const char * post_request="POST %s%s%s HTTP/1.0\r\nAccept: */*\r\nContent-Type: "
"application/x-www-form-urlencoded\r\nUser-Agent: Mozilla/4.0\r\nHost: %s\r\n"
"Content-Length: %u\r\nConnection: close\r\nPragma: no-cache\r\n%s\r\n%s" ;
char AddHeaders[ciMaxURLLen] = {0};
if (m_bUseProxy)
{
int pLoginLen = strlen(m_strProxyLogin);
int pPassLen = strlen(m_strProxyPassword);
if (pLoginLen && pPassLen)
{
char * proxyAuthInfo = new char [pLoginLen + pPassLen + 2];
if (proxyAuthInfo)
{
strcpy(proxyAuthInfo,m_strProxyLogin);
strcat(proxyAuthInfo,":" );
strcat(proxyAuthInfo,m_strProxyPassword);
CBase64Coder coder;
coder.Encode(proxyAuthInfo);
sprintf(AddHeaders,"Proxy-Authorization: Basic %s\r\n" , coder.EncodedMessage());
delete [] proxyAuthInfo;
} // if(proxyAuthInfo);
} // if(pLoginLen && pPassLen)
} // if(m_bUseProxy)
switch (method)
{
case rmGet:
{
if (m_bUseProxy)
sprintf(m_strWorkBuffer,get_request,url,"" ,"" ,crackedURL.GetHost(),AddHeaders);
else
{
int datalen = crackedURL.GetDataLength();
sprintf(m_strWorkBuffer,get_request,crackedURL.GetDocumentURL(),(datalen?"?" :"" )
,crackedURL.GetData(),crackedURL.GetHost(),AddHeaders);
}
_OUTPUT(m_strWorkBuffer);
}
break ; // case rmGet:
case rmPost:
{
int datalen = crackedURL.GetDataLength();
if (m_bUseProxy)
sprintf(m_strWorkBuffer,post_request,crackedURL.GetPrefix(),crackedURL.GetHost()
,crackedURL.GetDocumentURL(),crackedURL.GetHost(),datalen,AddHeaders,crackedURL.GetData());
else
{
sprintf(m_strWorkBuffer,post_request,crackedURL.GetDocumentURL(),"" ,"" ,crackedURL.GetHost()
,datalen,AddHeaders,crackedURL.GetData());
}
_OUTPUT(m_strWorkBuffer);
}
break ; // case rmPost:
} // switch(method)
unsigned long IP;
IP = LookupAddress(Host);
if (IP == INADDR_NONE)
return ReportError(HTTPREQ_CONNECT_FAILED);
SOCKET sock = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
if (INVALID_SOCKET == sock)
return ReportError(HTTPREQ_NO_CONNECTION);
sockaddr_in addr;
addr.sin_family=AF_INET;
addr.sin_port=htons(Port);
addr.sin_addr.s_addr=IP;
if (SOCKET_ERROR == ConnectEx(sock,(sockaddr *)&addr,sizeof (addr),m_iConnectTimeout*1000))
return ReportError(HTTPREQ_CONNECT_FAILED,sock);
int tmout = m_iReceiveTimeout*1000;
setsockopt(sock,SOL_SOCKET,SO_RCVTIMEO,(const char *)&tmout,sizeof (tmout));
tmout = m_iConnectTimeout*1000;
setsockopt(sock,SOL_SOCKET,SO_SNDTIMEO,(const char *)&tmout,sizeof (tmout));
/*
struct linger lin;
lin.l_onoff = lin.l_linger = 0;
setsockopt(FSocket, SOL_SOCKET, SO_LINGER, (char *) &lin,sizeof(linger));
*/
SendString(sock,m_strWorkBuffer);
m_strWorkBuffer[0] = 0;
DWORD dwStartTicks = ::GetTickCount();
int TotalBytesRead = 0;
bool GotHeaders = false ;
ReleaseBuffers();
int LastSearchPos = 0;
int ContentLength = 0;
int ReadedBodyBytes = 0;
bool NeedUpdateBodyBytes = false ;
int HeaderLength = 0;
DWORD ReceiveTimeoutPeriod = (DWORD)m_iReceiveTimeout*1000;
while (1)
{
if ((::GetTickCount() - dwStartTicks) > ReceiveTimeoutPeriod)
return ReportError(HTTPREQ_TIMEOUT,sock);
if (!IsReadible(sock))
{
if (m_pfnOnCancel && m_pfnOnCancel(m_OnCancelUserData))
return ReportError(HTTPREQ_CANCELLED);
Sleep(50);
continue ;
} // if(!IsReadible())
int BytesRead = recv(sock,m_strWorkBuffer,ciMaxURLLen*4,0);
if (m_pfnOnCancel && m_pfnOnCancel(m_OnCancelUserData))
return ReportError(HTTPREQ_CANCELLED);
if (SOCKET_ERROR == BytesRead)
return ReportError(GetLastError() == WSAETIMEDOUT?HTTPREQ_TIMEOUT:HTTPREQ_CONNECT_FAILED,sock);
if (!BytesRead)
break ;
if (!m_strRawResponse)
{
m_strRawResponse = new unsigned char [BytesRead + 1];
memcpy(m_strRawResponse,m_strWorkBuffer,BytesRead);
}
else
{
unsigned char * new_data = new unsigned char [TotalBytesRead + BytesRead + 1];
memcpy(new_data,m_strRawResponse,TotalBytesRead);
memcpy((new_data + TotalBytesRead),m_strWorkBuffer,BytesRead);
delete [] m_strRawResponse;
m_strRawResponse = new_data;
}
if (!GotHeaders && m_strRawResponse)
{
char * hdr_ptr = strstr((char *)(m_strRawResponse + LastSearchPos),"\r\n\r\n" );
GotHeaders = hdr_ptr!= NULL;
if (GotHeaders)
{
m_Headers.SetHeaders(m_strWorkBuffer, (hdr_ptr - m_strRawResponse));
const char * content_length = m_Headers.GetHeaderValue("Content-Length" );
if (content_length)
{
ContentLength = atoi(content_length);
if (ContentLength > 0)
{
HeaderLength = (hdr_ptr - m_strRawResponse) + 4;
ReadedBodyBytes = (TotalBytesRead + BytesRead) - HeaderLength;
}
} // if(content_length)
}// if(GotHeaders)
else
LastSearchPos += BytesRead;
} // if(!GotHeaders)
TotalBytesRead += BytesRead;
m_strRawResponse[TotalBytesRead] = 0;
if (GotHeaders && ContentLength > 0)
{
if (NeedUpdateBodyBytes)
ReadedBodyBytes += BytesRead;
else
NeedUpdateBodyBytes = true ;
if (ReadedBodyBytes >= ContentLength)
break ;
}
} // while(1)
if (!GotHeaders)
return ReportError(HTTPREQ_BAD_RESPONSE,sock);
m_iRawResponseSize = TotalBytesRead;
m_iBodySize = m_iRawResponseSize - HeaderLength;
_OUTPUT(m_strRawResponse);
m_strBodyPtr = (m_strRawResponse + HeaderLength);
FindResultCode();
CloseSocket(sock);
StopWSA();
return true ;
}
//---------------------------------------------------------------------------
bool CHTTPRequest::ReportError(int ResultCode,SOCKET s)
{
ReleaseBuffers();
if ( s != INVALID_SOCKET)
CloseSocket(s);
StopWSA();
return SetResultCode(ResultCode);
}
//---------------------------------------------------------------------------
bool CHTTPRequest::SetResultCode(int ResultCode)
{
m_iResultCode = ResultCode;
return false ;
}
//---------------------------------------------------------------------------
void CHTTPRequest::ReleaseBuffers()
{
if (m_strRawResponse)
delete [] m_strRawResponse;
m_strRawResponse = NULL;
m_strBodyPtr = NULL;
m_iRawResponseSize = 0;
m_iBodySize = 0;
m_iResultCode = 0;
}
//---------------------------------------------------------------------------
bool CHTTPRequest::IsReadible(SOCKET s)
{
timeval timeout = {0, 0};
fd_set fds;
FD_ZERO(&fds);
FD_SET(s, &fds);
int nStatus = select(0, &fds, NULL, NULL, &timeout);
if (nStatus == SOCKET_ERROR)
{
return false ;
}
else
{
return !(nStatus == 0);
}
}
//---------------------------------------------------------------------------
int CHTTPRequest::ConnectEx(SOCKET s, const struct sockaddr *name,int namelen, long timeout)
{
// connect(...) with timeout
// As connect() but with timeout setting.
int rc = 0;
ULONG ulB;
struct timeval Time;
fd_set FdSet;
ulB = TRUE; // Set socket to non-blocking mode
ioctlsocket(s, FIONBIO, &ulB);
if (connect(s, name, sizeof (SOCKADDR)) == SOCKET_ERROR) {
if (WSAGetLastError() == WSAEWOULDBLOCK) {
// now wait for the specified time
FD_ZERO(&FdSet);
FD_SET(s, &FdSet);
Time.tv_sec = timeout / 1000L;
Time.tv_usec = (timeout % 1000) * 1000;
rc = select(0, NULL, &FdSet, NULL, &Time);
}
}
ulB = FALSE; // Restore socket to blocking mode
ioctlsocket(s, FIONBIO, &ulB);
return (rc > 0) ? 0 : SOCKET_ERROR;
}
//---------------------------------------------------------------------------
void CHTTPRequest::CloseSocket(SOCKET s)
{
shutdown(s,0x02);
closesocket(s);
}
//---------------------------------------------------------------------------
bool CHTTPRequest::StartWSA()
{
WSADATA wsa;
return ( WSAStartup(MAKEWORD(1,0),&wsa)==0);
}
//---------------------------------------------------------------------------
void CHTTPRequest::FindResultCode()
{
const char * header0 = m_Headers.GetHeader(0);
if (header0)
{
char * ptr = strstr((char *)header0," " );
if (ptr)
{
ptr++;
int result = 0;
while (*ptr != ' ' )
{
result = result*10 + (*ptr - '0' );
ptr++;
} // while
m_iResultCode = result;
} // if(ptr)
}
}
//---------------------------------------------------------------------------
void CHTTPRequest::StopWSA()
{
WSACleanup();
}
//---------------------------------------------------------------------------
// CHTTPHeaders
//---------------------------------------------------------------------------
CHTTPHeaders::CHTTPHeaders()
{
m_strstrHeaders = NULL;
m_iHeadersCount = 0;
}
//---------------------------------------------------------------------------
CHTTPHeaders::~CHTTPHeaders()
{
Clear();
}
//---------------------------------------------------------------------------
void CHTTPHeaders::Clear()
{
if (!m_iHeadersCount || !m_strstrHeaders)
return ;
for (int i=0;i<m_iHeadersCount;i++)
delete [] m_strstrHeaders[i];
delete [] m_strstrHeaders;
m_strstrHeaders = NULL;
m_iHeadersCount = 0;
}
//---------------------------------------------------------------------------
const char * CHTTPHeaders::GetHeaderValue(int num) const
{
const char * header = GetHeader(num);
if (!header)
return NULL;
header = strstr(header,":" );
if (header)
{
header++;
if (*header == ' ' )
header++;
return header;
} // if(header)
return NULL;
}
//---------------------------------------------------------------------------
const char * CHTTPHeaders::GetHeaderValue(const char * header) const
{
if (!m_iHeadersCount || !m_strstrHeaders)
return NULL;
int len = strlen(header);
char * to_found = new char [len+2];
if (!to_found)
return NULL;
strcpy(to_found,header);
if (to_found[strlen(to_found)-1] != ':' )
strcat(to_found,":" );
len = strlen(to_found);
for (int i=0;i<m_iHeadersCount;i++)
{
if (!memicmp(m_strstrHeaders[i],to_found,len))
{
char * val = &(m_strstrHeaders[i][len]);
if (val)
{
val++;
if ( *val == ' ' )
val++;
delete [] to_found;
return val;
} // if(val)
break ;
} // if(!memicmp(m_strstrHeaders[i],to_found,len))
} // for
delete [] to_found;
return NULL;
}
//---------------------------------------------------------------------------
const char * CHTTPHeaders::GetHeader(int num) const
{
if (!m_iHeadersCount || !m_strstrHeaders || num >= m_iHeadersCount)
return NULL;
return m_strstrHeaders[num];
}
//---------------------------------------------------------------------------
void CHTTPHeaders::SetHeaders(const char * raw,int headers_len)
{
if (!raw || *raw == 0 || headers_len <=0)
return ;
Clear();
char * curr = (char *)raw;
while (1)
{
char * next_header = strstr(curr,"\r\n" );
if (next_header)
{
if ((next_header - raw) > headers_len)
break ;
m_iHeadersCount++;
} // if(next_header)
else
break ;
curr = next_header + 2;
} // while1
if (m_iHeadersCount)
{
m_strstrHeaders = new char *[m_iHeadersCount];
char * curr = (char *)raw;
for (int i=0;i<m_iHeadersCount;i++)
{
char * next = strstr(curr,"\r\n" );
if (!next)
break ;
int header_len = (next - curr);
m_strstrHeaders[i] = new char [header_len + 1];
m_strstrHeaders[i][header_len] = 0;
strncpy( m_strstrHeaders[i],curr,header_len);
m_strstrHeaders[i][header_len] = 0;
curr = next+2;
} // for
} // if(m_iHeadersCount)
}
//---------------------------------------------------------------------------
Все замечания, пожелания, тухлые помидоры, усовершенствования, оптимизации и пр. — велкам. Оченно хочется довести до простенького ума.
З.Ы. Каюсь — соответствие отсылаемого запроса RFC не проверял. Да и POST-запрос не тестировал. Еще одна оговорка — URL, передаваемый в MakeRequest, не должен содержать unsafe-characters. Вроде все.
Re[2]: Сел изобретать велосипед... :))
Здравствуйте, Flamer, Вы писали:
F>По просьбам трудящихся совсем сырые исходники того, что нужно было в первом посте... Никакой оптимизации, код сырой, все писалось за 3 часа на коленке. Сорри за объем кода. Итак:
Сырцы CBase64Coder здесь:
http://www.rsdn.ru/Forum/Message.aspx?mid=502584Автор: Flamer Дата: 10.01.04
Re: Сел изобретать велосипед... :))
От:
ilnar
Дата: 10.01.04 13:42
Оценка:
Здравствуйте, Flamer, Вы писали:
F>Что подскажут уважаемые?
здесь
здесь
Re[2]: Сел изобретать велосипед... :))
Здравствуйте, Flamer, Вы писали:
[]
Блин, пока просматривал свой пост, нашел пару косяков:
// 1
int Port = 80;
// заменить на
int Port = crackedURL.GetPort();
// 2
m_Headers.SetHeaders(m_strWorkBuffer , (hdr_ptr - m_strRawResponse));
// заменить на
m_Headers.SetHeaders(m_strRawResponse , (hdr_ptr - m_strRawResponse));
Re: Сел изобретать велосипед... :))
Здравствуйте, Flamer, Вы писали:
F>Вот понадобилась такая простенькая вещь:
F>1. Платформа — Windows.
F>2. Класс для получения информации по HTTP
F>3. Поддержка GET/POST
F>4. Поддержка HTTP-прокси и авторизации через прокси
F>5. Без использования высокоуровневых оберток типа CString и пр.
F>Вот думаю: изобретать или есть готовый на С/С++? В принципе, изобретено уже процентов 60, но терзают смутные сомнения, что все уже украдено до нас.
F>Размер — имеет большое значение
F>To moderator: я намеренно запостил в форум по С/С++, т.к. нужно именно код, на 99% написанный на С/С++.
F>Что подскажут уважаемые?
все уже придумано до нас
Alexander N. Treyner
Re[2]: Сел изобретать велосипед... :))
От:
ArtDenis
Дата: 10.01.04 17:14
Оценка:
Здравствуйте, Flamer, Вы писали:
F>По просьбам трудящихся совсем сырые исходники того, что нужно было в первом посте... Никакой оптимизации, код сырой, все писалось за 3 часа на коленке. Сорри за объем кода. Итак:
F>...
Эх... а я ожидал чего-то грандиозного
F>Все замечания, пожелания, тухлые помидоры, усовершенствования, оптимизации и пр. — велкам. Оченно хочется довести до простенького ума.
Пока особо внимательно не смотрел, но сразу обнаружил задачи, которые можно было бы обернуть в классы. Например сокеты. Кроме того, много сишного кода. Подобный код в деструкторе в современных плюсах вообще анахронизм:
if (m_strPrefix)
delete [] m_strPrefix;
Насчёт усовершенствования — можно было бы сделать потоки и пул потоков, чтобы можно было делать сразу несколько запросов.
Немгого погодя посмотрю повнимательнее, ещё что-нибудь скажу
... << RSDN@Home 1.1.0 stable >>
Re[3]: Сел изобретать велосипед... :))
Здравствуйте, ArtDenis, Вы писали:
AD>Пока особо внимательно не смотрел, но сразу обнаружил задачи, которые можно было бы обернуть в классы. Например сокеты.
Как-то оно не надо мне было
AD>Кроме того, много сишного кода.
Так это одно из требований к задаче у меня было...
AD>Подобный код в деструкторе в современных плюсах вообще анахронизм:
AD>AD>if (m_strPrefix)
AD> delete [] m_strPrefix;
AD>
Ну привык я так — чуть-что, везде проверять... Звиняйте...
AD>Насчёт усовершенствования — можно было бы сделать потоки и пул потоков, чтобы можно было делать сразу несколько запросов.
А зачем? Можно просто класс в поток и вперед...
Re[4]: Сел изобретать велосипед... :))
От:
ArtDenis
Дата: 10.01.04 17:50
Оценка:
Здравствуйте, Flamer, Вы писали:
AD>>Пока особо внимательно не смотрел, но сразу обнаружил задачи, которые можно было бы обернуть в классы. Например сокеты.
F>Как-то оно не надо мне было
Давай рассмотрим задачу с точки зрения здравого смысла. Что по идее должен делать класс CHTTPRequest? Ответ однозначный — делать запросы HTTP-серверу и принимать. И всё. Ну уж не как не стратовать/останавливать сокетную библиотеку, открывать/закрывать сокеты. Этот класс должен быть изолирован от низкоуровневой работы. Эту работу должен выполнять класс сокета. Следующее замечание. Этот класс хранит параметры запроса и ответ сервера. В принципе, можно так это и оставить, на запрос и ответ я бы тоже обернул в классы. Для чего это нужно? Всё очень просто: чтобы можно было хранить очередь запросов и очередь ответов.
AD>>Кроме того, много сишного кода.
F>Так это одно из требований к задаче у меня было...
А если не секрет, где это предъявляют такие требования?
AD>>Подобный код в деструкторе в современных плюсах вообще анахронизм:
AD>>AD>>if (m_strPrefix)
AD>> delete [] m_strPrefix;
AD>>
F>Ну привык я так — чуть-что, везде проверять... Звиняйте...
Скриплю зубами, но всё-таки извиняю
.
AD>>Насчёт усовершенствования — можно было бы сделать потоки и пул потоков, чтобы можно было делать сразу несколько запросов.
F>А зачем? Можно просто класс в поток и вперед...
Конечно можно. Но я только привык обегчать себе жизнь. Как только я вижу достаточно общую задачу, я сразу делаю соответствующую библиотеку.
... << RSDN@Home 1.1.0 stable >>
Re[5]: Сел изобретать велосипед... :))
От:
ArtDenis
Дата: 10.01.04 17:54
Оценка:
AD>Что по идее должен делать класс CHTTPRequest? Ответ однозначный — делать запросы HTTP-серверу и принимать .
Прочитал, и самому смешно стало
Конечно же имелось ввиду
"принимать ответы" ... << RSDN@Home 1.1.0 stable >>
Re: Сел изобретать велосипед... :))
Здравствуйте, Flamer, Вы писали:
F>Вот понадобилась такая простенькая вещь:
F>1. Платформа — Windows.
F>2. Класс для получения информации по HTTP
F>3. Поддержка GET/POST
F>4. Поддержка HTTP-прокси и авторизации через прокси
F>5. Без использования высокоуровневых оберток типа CString и пр.
F>Вот думаю: изобретать или есть готовый на С/С++? В принципе, изобретено уже процентов 60, но терзают смутные сомнения, что все уже украдено до нас.
F>Размер — имеет большое значение
F>To moderator: я намеренно запостил в форум по С/С++, т.к. нужно именно код, на 99% написанный на С/С++.
F>Что подскажут уважаемые?
Вот еще:
Tiny HTTP client
И не так плох WinInet. Я в HtmLayout его использую синхронно в отдельных потоках — эдакий resource pump. Пример описан в MSDN.
И вроде ничего так. Там есть плюс что он пользует централизованный cache и cookies storage.
Re[2]: Сел изобретать велосипед... :))
Здравствуйте, Flamer, Вы писали:
{skip}
А как насчёт HTTP/1.0 и chunked trasfer encoding?
Просто запросы в 1.0 могут сильно грузить сервер,
особенно в случае всяких там скриптов и иже с ними.
Опять таки keep alive сильно всё ускоряет,
особенно proxy keep alive
Я просто писал похожую штуку, и можно сказать собаку съел на этом
Несколько полезных идей: Content-Type handler, функция
которая по контент тайпу ответа определяет что делать
с ответом. Ибо какой-нибудь application/x-zip размером 100Mb,
наверное стоит сразу писать в файло, а в случае статуса 404 ответ
в общем-то совсем не нужен...
Почти наверняка понадабятся такие вещи как Cookie и Referer.
Свои сорки в общем-то запостить могу,
но разве что для целей ознакомления,
ибо они зависят от целой кучи других либ...
Re[3]: Сел изобретать велосипед... :))
От:
dad
Дата: 12.01.04 05:14
Оценка:
сокеты. Кроме того, много сишного кода. Подобный код в деструкторе в современных плюсах вообще анахронизм:
AD>AD>if (m_strPrefix)
AD> delete [] m_strPrefix;
AD>
вот оно тебе надо флейм этот раздувать
Веру-ю-у! В авиацию, в научную революци-ю-у, в механизацию сельского хозяйства, в космос и невесомость! Веру-ю-у! Ибо это объективно-о! (Шукшин)
Re[2]: Сел изобретать велосипед... :))
От:
Lexey
Дата: 10.02.04 13:34
Оценка:
Здравствуйте, IT, Вы писали:
IT>А чем WinInet плох?
Многим.
Меня вот удивляет, что никто про WinHTTP не вспомнил. А это как раз прямая замена WinInet от MS. И умеет она все, что Flamer описал.
... << RSDN@Home 1.1.2 stable >>
Пока на собственное сообщение не было ответов, его можно удалить.
Удалить