Re: да, нужна такая библиотека.
От: Lexey Россия  
Дата: 16.02.04 17:21
Оценка: 12 (1)
Здравствуйте, Denis, Вы писали:

D>5.1 не имеет редистрибьюта и требует последних систем(WinXP SP1, Win2003, Win2k SP3(4 забыл какой точно))


Вот тут MS как всегда привирает. В составе SOAP Toolkit 3.0 идет merge-модуль WinHTTP 5.1 который ставится и работает даже на NT4. Сам его уже довольно долго использую.

D>WinInet не умеет работать с сервисами... так что почти не жилец.


Дык.
... << RSDN@Home 1.1.2 stable >>
Re: Сел изобретать велосипед... :))
От: m.a.g. Мальта http://dottedmag.net/
Дата: 10.01.04 06:35
Оценка: 8 (1)
Здравствуйте, Flamer, Вы писали:

F>2. Класс для получения информации по HTTP


Посмотри libwww от w3c.
... << RSDN@Home 1.1.0 stable >>
Re: Сел изобретать велосипед... :))
От: c-smile Канада http://terrainformatica.com
Дата: 11.01.04 21:17
Оценка: 8 (1)
Здравствуйте, 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: Сел изобретать велосипед... :))
От: Flamer Кипр http://users.livejournal.com/_flamer_/
Дата: 10.01.04 13:36
Оценка: 2 (1)
Здравствуйте, 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. Вроде все.
Сел изобретать велосипед... :))
От: Flamer Кипр http://users.livejournal.com/_flamer_/
Дата: 09.01.04 17:35
Оценка:
Вот понадобилась такая простенькая вещь:

1. Платформа — Windows.
2. Класс для получения информации по HTTP
3. Поддержка GET/POST
4. Поддержка HTTP-прокси и авторизации через прокси
5. Без использования высокоуровневых оберток типа CString и пр.

Вот думаю: изобретать или есть готовый на С/С++? В принципе, изобретено уже процентов 60, но терзают смутные сомнения, что все уже украдено до нас.

Размер — имеет большое значение

To moderator: я намеренно запостил в форум по С/С++, т.к. нужно именно код, на 99% написанный на С/С++.

Что подскажут уважаемые?

10.01.04 16:17: Перенесено из 'C/C++'
Re: Сел изобретать велосипед... :))
От: IT Россия linq2db.com
Дата: 10.01.04 07:30
Оценка:
Здравствуйте, Flamer, Вы писали:

F>Вот понадобилась такая простенькая вещь:


F>1. Платформа — Windows.

F>2. Класс для получения информации по HTTP
F>3. Поддержка GET/POST
F>4. Поддержка HTTP-прокси и авторизации через прокси
F>5. Без использования высокоуровневых оберток типа CString и пр.

А чем WinInet плох?
Если нам не помогут, то мы тоже никого не пощадим.
Re[2]: Сел изобретать велосипед... :))
От: kliff Россия http://www.esignal.ru
Дата: 10.01.04 10:06
Оценка:
Здравствуйте, IT, Вы писали:

IT>Здравствуйте, Flamer, Вы писали:


F>>Вот понадобилась такая простенькая вещь:


F>>1. Платформа — Windows.

F>>2. Класс для получения информации по HTTP
F>>3. Поддержка GET/POST
F>>4. Поддержка HTTP-прокси и авторизации через прокси
F>>5. Без использования высокоуровневых оберток типа CString и пр.

IT>А чем WinInet плох?


Если синхронный инет — хреново, асинхронный — геморно. Есть у меня класс на асинк-инете, но он написан хреново.. Работает хорошо, но слишком много там исправлений всяких. Если что-то серьезное качать — надо переписывать. Могу сбросить как семпл..
Re[2]: Сел изобретать велосипед... :))
От: Flamer Кипр http://users.livejournal.com/_flamer_/
Дата: 10.01.04 11:47
Оценка:
Здравствуйте, 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[2]: Сел изобретать велосипед... :))
От: Flamer Кипр http://users.livejournal.com/_flamer_/
Дата: 10.01.04 13:39
Оценка:
Здравствуйте, 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 Кипр http://users.livejournal.com/_flamer_/
Дата: 10.01.04 13:53
Оценка:
Здравствуйте, 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: Сел изобретать велосипед... :))
От: sndralex Израиль www.gdetotam.co.il
Дата: 10.01.04 14:29
Оценка:
Здравствуйте, 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]: Сел изобретать велосипед... :))
От: Flamer Кипр http://users.livejournal.com/_flamer_/
Дата: 10.01.04 17:31
Оценка:
Здравствуйте, 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[2]: Сел изобретать велосипед... :))
От: e-Xecutor Россия  
Дата: 12.01.04 04:55
Оценка:
Здравствуйте, 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 >>
Re[3]: Сел изобретать велосипед... :))
От: AndrewJD США  
Дата: 11.02.04 07:59
Оценка:
Здравствуйте, Lexey, Вы писали:


IT>>А чем WinInet плох?


L>Многим. Меня вот удивляет, что никто про WinHTTP не вспомнил. А это как раз прямая замена WinInet от MS. И умеет она все, что Flamer описал.


Ну многим пишут софт который под 9х виндой работает
"For every complex problem, there is a solution that is simple, neat,
and wrong."
Re[4]: Сел изобретать велосипед... :))
От: Lexey Россия  
Дата: 11.02.04 16:13
Оценка:
Здравствуйте, AndrewJD, Вы писали:

L>>Многим. Меня вот удивляет, что никто про WinHTTP не вспомнил. А это как раз прямая замена WinInet от MS. И умеет она все, что Flamer описал.


AJD>Ну многим пишут софт который под 9х виндой работает


На ней можно XMLHttp юзать, хотя он конечно горазбо более убогий, чем WinHTTP.
... << RSDN@Home 1.1.2 stable >>
да, нужна такая библиотека.
От: Denis Россия http://blogs.gotdotnet.ru/personal/Denis
Дата: 14.02.04 22:23
Оценка:
L>На ней можно XMLHttp юзать, хотя он конечно горазбо более убогий, чем WinHTTP.

плюсы WinHTTP
— и в серверном и в клиенском можно применять
— удобно реализована autoproxy
минусы WinHTTP
— com библиотека по умолчание не заренистрированна в WIn2003 (кошмар на самом деле)
— баркдак с версиями:
5.0 есть редистрибьют и ставится куда угодно, но имеет кучу ограничений(обидное на TLS ограничение) и не поддерживается MS
5.1 не имеет редистрибьюта и требует последних систем(WinXP SP1, Win2003, Win2k SP3(4 забыл какой точно))

итого имеет слабо применимый вариант.(хотя я скрипя зубами использую 5.1)

WinInet не умеет работать с сервисами... так что почти не жилец.

>Вот думаю: изобретать или есть готовый на С/С++?

Так что нужна такая библиотека. только хорошо бы в ней основные фичи WinHTTP реализовать.
... << RSDN@Home 1.1 beta 1 >>
Re: Сел изобретать велосипед... :))
От: Stoune  
Дата: 21.03.05 23:05
Оценка:
Здравствуйте, Flamer, Вы писали:

F>Вот понадобилась такая простенькая вещь:


F>To moderator: я намеренно запостил в форум по С/С++, т.к. нужно именно код, на 99% написанный на С/С++.

А мне нужно было быстро сделать похожую задачу, так я всё на Python сделал, а потом в плюсы результат отдал.
F>Что подскажут уважаемые?
... << RSDN@Home 1.1.4 beta 4 rev. 303>>
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.