📄 chttpclient.c
字号:
/*************************************************************************** CHttpClient.c Advanced Network component (c) 2003-2004 Daniel Campos Fernández <danielcampos@netcourrier.com> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.***************************************************************************/#define __CHTTPCLIENT_C#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <curl/curl.h>#include <curl/easy.h>#include <curl/multi.h>#include "main.h"#include "gambas.h"#include "CCurl.h"#include "CHttpClient.h"#include "CProxy.h"/***************************************************** CURLM : a pointer to use curl_multi interface, allowing asynchrnous work without using threads in this class. Here also a pipe will be stablished to link with Gambas watching interface ******************************************************/extern GB_STREAM_DESC CurlStream;extern CURLM *CCURL_multicurl;/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////*******************************************************************#################################################################### CALLBACKS FROM CURL LIBRARY####################################################################********************************************************************/void http_parse_header(CHTTPCLIENT *mythis){ /* Obtains HTTP return code and return string */ int myloop; int mypos=0; int npos=0; int nposend=0; int len; char *buf; len=strlen(mythis->buf_header[0]); buf=mythis->buf_header[0]; for (myloop=4;myloop<len;myloop++) { if (buf[myloop]==' ') { mypos=myloop+1; break; } } if (!mypos) return; for (myloop=mypos;myloop<len;myloop++) { if (buf[myloop]==' ') { if (npos) { npos=myloop+1; break; } } else { if ( !((buf[myloop]>47) && (buf[myloop]<58)) ) { if (buf[myloop] != 13) return; else return; } else { npos++; if (npos>3) return; mythis->ReturnCode*=10; mythis->ReturnCode+=(buf[myloop]-48); } } } GB.Alloc((void**)&mythis->ReturnString,(nposend+1)*sizeof(char)); mythis->ReturnString[nposend]=0; for (myloop=npos;myloop<(nposend+npos);myloop++) mythis->ReturnString[myloop-npos]=buf[myloop];}int http_header_curl(void *buffer, size_t size, size_t nmemb, void *_object){ if (!THIS->len_header) { GB.Alloc((void**)&THIS->buf_header,sizeof(char*)); GB.Alloc((void**)&THIS->buf_header[0],nmemb+1); } else { GB.Realloc((void**)&THIS->buf_header,sizeof(char*)*(1+THIS->len_header)); GB.Alloc((void**)&(THIS->buf_header[THIS->len_header]),nmemb+1); THIS->buf_header[THIS->len_header][nmemb]=0; } strncpy(THIS->buf_header[THIS->len_header],buffer,nmemb); THIS->len_header++; if (THIS_STATUS==6) { THIS_STATUS=4; GB.Ref(THIS); GB.Post(CCURL_raise_connect,(long)THIS); } return nmemb;}int http_write_curl(void *buffer, size_t size, size_t nmemb, void *_object){ if (!THIS->ReturnCode) http_parse_header(THIS); if (THIS_FILE) { return fwrite(buffer,size,nmemb,THIS_FILE); } else { if (!THIS->len_data) GB.Alloc((void**)&THIS->buf_data,nmemb); else GB.Realloc((void**)&THIS->buf_data,nmemb+THIS->len_data); memcpy(THIS->buf_data+THIS->len_data,buffer,nmemb); THIS->len_data+=nmemb; } GB.Ref(THIS); GB.Post(CCURL_raise_read,(long)THIS); return nmemb;}//////////////////////////////////////////////////////////////////////////########################## Other properties ##########################/////////////////////////////////////////////////////////////////////////***************************************************************** URL to work with *****************************************************************///////////////////////////////////////////////////////////////////////////########################## Cookies ###################################////////////////////////////////////////////////////////////////////////BEGIN_PROPERTY ( CHttpClient_UpdateCookies ) if (READ_PROPERTY) { GB.ReturnBoolean(THIS->updatecookies); return; } if (THIS_STATUS > 0) { GB.Error ("UpdateCookies property can not be changed while working"); return; } if (VPROP(GB_BOOLEAN)) THIS->updatecookies=1; else THIS->updatecookies=0;END_PROPERTYBEGIN_PROPERTY ( CHttpClient_CookiesFile ) if (READ_PROPERTY) { GB.ReturnNewString(THIS->cookiesfile,0); return; } if (THIS_STATUS > 0) { GB.Error ("CookiesFile property can not be changed while working"); return; } if (THIS->cookiesfile) { GB.Free((void**)&THIS->cookiesfile); THIS->cookiesfile=NULL; } if ( strlen(GB.ToZeroString(PROP(GB_STRING))) ) { GB.Alloc( (void**)&THIS->cookiesfile, \ (strlen(GB.ToZeroString(PROP(GB_STRING)))+1)*sizeof(char)); strcpy(THIS->cookiesfile,GB.ToZeroString(PROP(GB_STRING))); }END_PROPERTY/*********************************************** HTTP authentication : Plain, GSS negotiation, Digest or NTLM************************************************/BEGIN_PROPERTY (CHttpClient_Auth) if (READ_PROPERTY) { GB.ReturnInteger(THIS->auth); return; } if (THIS_STATUS > 0) { GB.Error ("Auth property can not be changed while working"); return; } if (Adv_user_SETAUTH (&THIS->user,VPROP(GB_INTEGER)) ) GB.Error ("Unknown authentication method"); else THIS->auth=VPROP(GB_INTEGER);END_PROPERTY/********************************************* User Agent string to be sent to the server *********************************************/BEGIN_PROPERTY ( CHttpClient_UserAgent ) if (READ_PROPERTY) { GB.ReturnString(THIS->sUserAgent); return; } if (THIS_STATUS > 0) { GB.Error ("UserAgent property can not be changed while working"); return; } GB.StoreString(PROP(GB_STRING), &THIS->sUserAgent);END_PROPERTY/********************************************* Return code received from Http Server *********************************************/BEGIN_PROPERTY ( CHttpClient_ReturnCode ) GB.ReturnInteger(THIS->ReturnCode);END_PROPERTY/********************************************* Return string received from Http Server *********************************************/BEGIN_PROPERTY ( CHttpClient_ReturnString ) GB.ReturnNewString(THIS->ReturnString,0);END_PROPERTY/********************************************* Header of data received from Http Server *********************************************/BEGIN_PROPERTY ( CHttpClient_Headers ) GB_ARRAY retval; char *element; int myloop; if ( (THIS_STATUS==4) || (THIS_STATUS==0) ) { if (THIS->len_header) { GB.Array.New(&retval,GB_T_STRING,THIS->len_header); for (myloop=0;myloop<THIS->len_header;myloop++) { GB.NewString(&element,THIS->buf_header[myloop],strlen(THIS->buf_header[myloop])); *((char **)GB.Array.Get(retval,myloop)) = element; } GB.ReturnObject ( retval ); return; } }END_PROPERTY//*************************************************************************//#################### INITIALIZATION AND DESTRUCTION #####################//*************************************************************************/************************************************* Gambas object "Constructor" *************************************************/BEGIN_METHOD_VOID(CHTTPCLIENT_new) char *tmp=NULL; GB.Alloc((void**)&tmp,sizeof(char)*(1+strlen("http://127.0.0.1:80"))); strcpy(tmp,"http://127.0.0.1:80"); THIS_URL=tmp; GB.NewString(&THIS->sUserAgent,"Gambas Http/1.0",0); tmp=NULL; GB.Alloc((void**)&tmp,8); strcpy(tmp,"http://"); THIS_PROTOCOL=tmp;END_METHOD/************************************************* Gambas object "Destructor" *************************************************/BEGIN_METHOD_VOID(CHTTPCLIENT_free) http_reset(THIS); if (THIS->sUserAgent) GB.FreeString(&THIS->sUserAgent); if (THIS->cookiesfile) GB.Free((void**)&THIS->cookiesfile); if (THIS->ReturnString) GB.Free((void**)&THIS->ReturnString); END_METHOD//*************************************************************************//#################### METHODS ############################################//*************************************************************************/************************************************* GET HTTP method *************************************************/void http_initialize_curl_handle(void *_object){ if (THIS_CURL) { if (Adv_Comp ( THIS->user.userpwd,THIS->user.user,THIS->user.pwd)) { CCURL_stop(_object); http_reset(_object); THIS_CURL=curl_easy_init(); } } else { THIS_CURL=curl_easy_init(); } curl_easy_setopt(THIS_CURL, CURLOPT_PRIVATE,(char*)_object); curl_easy_setopt(THIS_CURL, CURLOPT_USERAGENT,THIS->sUserAgent); curl_easy_setopt(THIS_CURL, CURLOPT_HEADERFUNCTION, http_header_curl); curl_easy_setopt(THIS_CURL, CURLOPT_WRITEFUNCTION, http_write_curl); curl_easy_setopt(THIS_CURL, CURLOPT_WRITEDATA, _object); curl_easy_setopt(THIS_CURL, CURLOPT_WRITEHEADER, _object); curl_easy_setopt(THIS_CURL, CURLOPT_COOKIEFILE, THIS->cookiesfile); if (THIS->updatecookies) curl_easy_setopt(THIS_CURL, CURLOPT_COOKIEJAR, THIS->cookiesfile); else curl_easy_setopt(THIS_CURL, CURLOPT_COOKIEJAR, NULL); Adv_proxy_SET (&THIS->proxy->proxy,THIS_CURL); Adv_user_SET (&THIS->user, THIS_CURL); curl_easy_setopt(THIS_CURL, CURLOPT_URL,THIS_URL); THIS->ReturnCode=0; if (THIS->ReturnString) { GB.Free((void**)&THIS->ReturnString); THIS->ReturnString=NULL; } http_reset(_object); THIS_STATUS=6; THIS->stream.desc=&CurlStream;}int http_get (void *_object){ if (THIS_STATUS > 0) return 1; THIS->iMethod=0; http_initialize_curl_handle(_object); curl_easy_setopt(THIS_CURL,CURLOPT_HTTPGET,1); curl_multi_add_handle(CCURL_multicurl,THIS_CURL); CCURL_init_post(); return 0;}BEGIN_METHOD(CHTTPCLIENT_Get,GB_STRING TargetHost;) if (!MISSING(TargetHost)) { if (THIS_STATUS > 0) { GB.Error("Still active"); return; } THIS_FILE=fopen(STRING(TargetHost),"w"); if (!THIS_FILE) { GB.Error("Unable to open file for writing"); return; } } if (http_get(THIS)) { GB.Error("Still active"); return; }END_METHOD/************************************************* POST HTTP method *************************************************/int http_post (void *_object,char *sContent,char *sData,int lendata){ int mylen; struct curl_slist *headers=NULL; int myloop; if (THIS_STATUS > 0) return 1; if (!sContent) return 2; if (!sData) return 3; for (myloop=0;myloop<strlen(sContent);myloop++) if (sContent[myloop]<32) return 1; http_initialize_curl_handle(_object); mylen=strlen(sContent) + strlen("Content-Type: ") + 1; GB.Alloc((void*)&THIS->sContentType,mylen); GB.Alloc((void*)&THIS->sPostData,lendata+1); strncpy(THIS->sPostData,sData,lendata); THIS->sContentType[0]=0; strcpy(THIS->sContentType,"Content-Type: "); strcat(THIS->sContentType,sContent); THIS->iMethod=1; headers=curl_slist_append(headers,THIS->sContentType ); curl_easy_setopt(THIS_CURL,CURLOPT_POSTFIELDS,THIS->sPostData); curl_easy_setopt(THIS_CURL,CURLOPT_POSTFIELDSIZE,lendata); curl_easy_setopt(THIS_CURL,CURLOPT_HTTPHEADER,headers); curl_multi_add_handle(CCURL_multicurl,THIS_CURL); CCURL_init_post(); return 0;}BEGIN_METHOD(CHTTPCLIENT_Post,GB_STRING sContentType;GB_STRING sData;GB_STRING TargetHost;) if (!MISSING(TargetHost)) { if (THIS_STATUS > 0) { GB.Error("Still active"); return; } THIS_FILE=fopen(STRING(TargetHost),"w"); if (!THIS_FILE) { GB.Error("Unable to open file for writing"); return; } } switch(http_post (THIS,STRING(sContentType),STRING(sData),LENGTH(sData)) ) { case 1: GB.Error("Still active"); return; case 2: GB.Error("Invalid content type"); return; case 3: GB.Error("Invalid data"); return; }END_METHODvoid http_reset(void *_object){ int myloop; if (THIS->buf_data) { GB.Free((void**)&THIS->buf_data); THIS->buf_data=NULL; } if (THIS->buf_header) { for (myloop=0;myloop < THIS->len_header;myloop++) GB.Free((void**)&THIS->buf_header[myloop]); GB.Free((void**)&THIS->buf_header); THIS->buf_header=NULL; } if (THIS->sContentType) { GB.Free((void**)&THIS->sContentType); THIS->sContentType=NULL; } if (THIS->sPostData) { GB.Free((void**)&THIS->sPostData); THIS->sPostData =NULL; } THIS->len_data=0; THIS->len_header=0;}BEGIN_METHOD_VOID(CHTTPCLIENT_Stop) CCURL_stop(THIS); http_reset(_object);END_METHOD//*************************************************************************//#################### GAMBAS INTERFACE ###################################//*************************************************************************GB_DESC CHttpClientDesc[] ={ GB_DECLARE("HttpClient", sizeof(CHTTPCLIENT)), GB_INHERITS("Curl"), GB_METHOD("_new", NULL, CHTTPCLIENT_new, NULL), GB_METHOD("_free", NULL, CHTTPCLIENT_free, NULL), GB_METHOD("Stop", NULL, CHTTPCLIENT_Stop, NULL), GB_METHOD("Get", NULL, CHTTPCLIENT_Get, "[(TargetFile)s]"), GB_METHOD("Post", NULL, CHTTPCLIENT_Post, "(ContentType)s(Data)s[(TargetFile)s]"), GB_PROPERTY("Auth","i<Net,AuthNone,AuthBasic,AuthNTLM,AuthDIGEST,AuthGSSNEGOTIATE>",CHttpClient_Auth), GB_PROPERTY("CookiesFile", "s",CHttpClient_CookiesFile), GB_PROPERTY("UpdateCookies", "b",CHttpClient_UpdateCookies), GB_PROPERTY("Headers", "String[]", CHttpClient_Headers), GB_PROPERTY("UserAgent", "s", CHttpClient_UserAgent), GB_PROPERTY_READ("Code","i",CHttpClient_ReturnCode), GB_PROPERTY_READ("Reason","s",CHttpClient_ReturnString), GB_CONSTANT("_Properties", "s", HTTP_PROPERTIES), GB_CONSTANT("_DefaultEvent", "s", "Read"), GB_END_DECLARE};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -