Clase para HTTP

Post Reply
AndresRomero
Posts: 11
Joined: Fri Nov 20, 2009 11:07 am

Clase para HTTP

Post by AndresRomero »

¿ Existe alguna clase para el protocolo HTTP como existe para FTP, SMTP, etc ?

Yo he hecho una pero no se si es mejorable, o existe alguna que no conozco y poder utilizarla.
Si existe y alguien lo conoce, por favor decirmelo.
Si no existe y alguien la quiere puedo compartirla para mejorarla.

Un saludo
Andrés Romero
Carlos Mora
Posts: 988
Joined: Thu Nov 24, 2005 3:01 pm
Location: Madrid, España

Re: Clase para HTTP

Post by Carlos Mora »

Hola Andrés!

En las contribs de Harbour hay una clase muy interesante para usar del lado del servidor, hasta con multihilos. se llama hbhttpd.

¿En que estabas pensando, servidor o cliente?
Saludos
Carlos Mora
http://harbouradvisor.blogspot.com/
StackOverflow http://stackoverflow.com/users/549761/carlos-mora
“If you think education is expensive, try ignorance"
AndresRomero
Posts: 11
Joined: Fri Nov 20, 2009 11:07 am

Re: Clase para HTTP

Post by AndresRomero »

Hola Carlos, es para usar como cliente. En servidor tengo Apache con PHP y SQL

La ventaja de usar esta clase como cliente es que se puede establecer un "diálogo" entre el servidor y el cliente con cualquier cosa siempre que lo programes en ambas partes.

La clase que yo he hecho es muy sencilla, por eso me extraña que nadie lo tenga ya. Quizás es que no he sabido buscar bien.

Carlos, gracias por tu rápida respuesta.
User avatar
Antonio Linares
Site Admin
Posts: 37481
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: Clase para HTTP

Post by Antonio Linares »

Andrés,

FWH proporciona la Clase TWebClient y tienes un ejemplo de uso en samples\webclient.prg

Posiblemente entre la tuya y la nuestra, podamos mejorar ambas :-)
regards, saludos

Antonio Linares
www.fivetechsoft.com
AndresRomero
Posts: 11
Joined: Fri Nov 20, 2009 11:07 am

Re: Clase para HTTP

Post by AndresRomero »

Gracias Antonio,
He visto la clase TWebClient. Voy a tratar de hacer una clase con lo mejor de las dos. Cuando lo tenga hecho y probado os lo comento.
Un saludo
Carlos Mora
Posts: 988
Joined: Thu Nov 24, 2005 3:01 pm
Location: Madrid, España

Re: Clase para HTTP

Post by Carlos Mora »

Andrés,

échale una mirada a hb_curl. Es muy completa, y puedes hacer virtualmente cualquier cosa, incluyendo POST/GET, tambien es cliente FTP/POP. Maneja el tema de autenticación, puertos, etc.
La uso para hacer presentaciones en la Agencia Tributaria, y funciona como la seda.

Un saludo
Saludos
Carlos Mora
http://harbouradvisor.blogspot.com/
StackOverflow http://stackoverflow.com/users/549761/carlos-mora
“If you think education is expensive, try ignorance"
AndresRomero
Posts: 11
Joined: Fri Nov 20, 2009 11:07 am

Re: Clase para HTTP

Post by AndresRomero »

Carlos, muchas gracias por tu interés. He bajado la clase o al menos lo que yo creo que es, y no la entiendo. Creo que una de dos:
a) yo soy muy simple (lo más probable).
b) la clase es muy complicada.

Si alguien esta interesado en mi modesta clase, pedirmelo a "andres.romero@puentelibros.com" para no ocupar este foro.

Antonio, si estas interesado dímelo. Podríamos decir que la clase es una extensión de la vuestra con GET, POST, parámetros, etc.
User avatar
Antonio Linares
Site Admin
Posts: 37481
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: Clase para HTTP

Post by Antonio Linares »

Andrés,

Si la copias aqui te lo agradecemos, asi queda disponible para quien pueda interesarle, gracias! :-)
regards, saludos

Antonio Linares
www.fivetechsoft.com
AndresRomero
Posts: 11
Joined: Fri Nov 20, 2009 11:07 am

Re: Clase para HTTP

Post by AndresRomero »

Es la primera vez que "intento" poner un prg, a ver como me sale :oops:

Code: Select all

* THTTP por Andrés Romero García (ARG) andres.romero@puentelibros.com

* Ver ejemplos al final del programa
* ==================================

#include "FiveWin.ch"

#include "romewin.inc"  /* Se puede sustituir por:

                            #define CRLF    chr(13)+chr(10)
                            #define SI      .T.
                            #define NO      .F.

                           El resto del include, no se utiliza en esta clase
                        */

memvar oWnd

//----------------------------------------------------------------------------//

CLASS THTTP

// datos de la clase:
    DATA    oSocket     // socket usado durante la sesion.
    DATA    lSocket     // bandera de haberse creado o no el socket.
    DATA    nSocket     // numero del socket.
    DATA    nPort       // puerto de comunicación. Por defecto, el 80.
    DATA    cIPServer   // IP del servidor.
    DATA    nDelay      // demora intencionada en la recepción de los paquetes.
    DATA    nTiempo     // tiempo empleado en recibir el recurso.
    DATA    lAvisa      // bandera de avisar o no cuando hay timeout.
    DATA    lTimeOut    // error por exceso de tiempo.
    DATA    lTrozos     // respuesta en trozos.
    DATA    nTiempoMax  // tiempo maximo de espera antes de dar error por TimeOut.
    DATA    cCabecera   // cabecera de la respuesta del servidor.
    DATA    cDatos      // datos en la respuesta del servidor.
    DATA    nLargo      // longitud del recurso.
    DATA    lLargo      // definido por longitud.
    DATA    cRecurso    // recurso devuelto en la respuesta del servidor.
    DATA    lMeter      // bandera de utilizar meter o no.
    DATA    cAgente     // texto libre que se manda dentro de la peticion HTTP bajo el apartado "User-Agent".

// datos propios del protocolo HTTP. Son respuestas que se reciben del servidor cuando se hace una consulta:
    DATA    h_HTTP
    DATA    h_RESN              // código de la respuesta en formato númerico, por ejemplo 200, 404, etc.
    DATA    h_RESC              // código de la respuesta en formato texto, por ejemplo, OK, "Página no encontrada", etc. (Lo que importa es el número).
    DATA    h_DATE
    DATA    h_SERVER
    DATA    h_LOCATION
    DATA    h_LAST_MODIFIED
    DATA    h_ETAG
    DATA    h_ACCEPT_RANGES
    DATA    h_CONTENT_LENGTH
    DATA    h_CONTENT_TYPE

// metodos:
    METHOD New( cIPServer, nPort ) CONSTRUCTOR      // crea la clase y conecta por medio de un socket.
    METHOD SendData( cComando )                     // manda una solicitud HTTP al servidor. El texto del comando lo tenemos que componer fuera de la clase
    METHOD GetData( cComando )                      // manda una solicitud y recibe el recurso HTTP devuelto por el servidor.
    METHOD End()                                    // termina la clase y el socket asociado.
    METHOD get(cDireccion,cRecurso)                 // envia una petición por el metodo GET. El comando se crea en el método. Más rápido pero menos flexible.
    METHOD post(cDireccion,cRecurso,cPost)          // envia una petición por el metodo POST. El comando se crea en el método. Más rápido pero menos flexible.

* Siempre que se pueda, usar post() mejor que get()

ENDCLASS

//----------------------------------------------------------------------------//

// la direccion puede ser por IP (84.124.52.140) o por dominio (argcon.net). El puerto por defecto será el 1234. No confundir con el puerto 80

METHOD New( cIPServer, nPort, abc ) CLASS THTTP

local cData

DEFAULT nPort to 1234   // puerto por el que salimos nosotros a internet. NO CONFUNDIR con el 80 que no tiene nada que ver.

::nPort     := nPort
::oSocket   := TSocket():New( ::nPort )                     // hacer esto ANTES que GetHostByName()
::lSocket   := ValType( ::oSocket ) == "O" .and. ::oSocket:nSocket > 0
::nSocket   := ::oSocket:nSocket
::cIPServer := GetHostByName( cIPServer )                   // para tener la IP desde el nombre, SIEMPRE tiene que crear socket antes.
::lTimeOut  := NO
::nTiempoMax:= 5
::cCabecera := ''
::cRecurso  := ''
::nDelay    := 0.05
::nTiempo   := 0
::lAvisa    := SI
::lLargo    := NO
::lTrozos   := NO
::lMeter    := NO
::cAgente   := 'ARG-Consulting' // esto es libre, por ejemplo, los navegadores ponen aquí el tipo de navegador MSIE, o CROME, etc.

::h_HTTP            := ''
::h_RESN            := 0        // SOLO este dato es obligatorio. Del resto, algunos servidores mandan lo que quieren.
::h_RESC            := ''
::h_DATE            := ''
::h_SERVER          := ''
::h_LOCATION        := ''
::h_LAST_MODIFIED   := ''
::h_ETAG            := ''
::h_ACCEPT_RANGES   := ''
::h_CONTENT_LENGTH  := ''
::h_CONTENT_TYPE    := ''

::oSocket:Connect( ::cIpServer, 80 )    // direccion y puerto por el que escucha el servidor de internet. (p.e. 84.124.52.140, 80 )

inkey(0.5)

cData := ::oSocket:GetData()    // "limpia" el buffer de entrada para empezar de cero.

return self

//----------------------------------------------------------------------------//

METHOD SendData(cComando) CLASS THTTP

if !empty( cComando )
    ::oSocket:SendData( cComando )
endif

return NIL

//----------------------------------------------------------------------------//

METHOD GetData(cComando) CLASS THTTP
/*
    Si el recurso se recibe en trozos ("chunked"), se van poniendo como elementos de un array.
    Cuando llega el ultimo trozo, junta todos los elementos y forma el recurso que será entregado
    Si no se pusiera en array y se quiere formar el recurso a medida que se recibe, el ordenador no
        tiene tiempo y corta el recurso de mala manera.

// Esta línea se puede poner en cualquier parte del PRG para ayudarnos a hacer una pequeña depuración///////////////////
nCanal := fcreate('.\http'+str0s(++nConCanal,3)+'.txt') ; fwrite(nCanal,cData) ; fclose(nCanal)                       //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
*/
local cData     := ''
local nSegundos := seconds() + ::nTiempoMax
local cCabeMayu         // cabecera recibida convertida a mayçusculas para que sea más fácil buscar
local nLineas
local nConta    := 0
local nPun

local aRecurso  := {}   // array de los "trozos" del recurso recibido
local nI        := 0
local nL        := 0
local nLen      := 0
local cLinea    := ''
local cMayus    := ''

local cFinal    := CRLF+'0'+CRLF+CRLF

local nActual   := 0
local oMeter

local nConCanal  := 0  // contador para la grabación de paquetes

::nTiempo   := seconds()
::lTimeOut  := NO
::lLargo    := NO
::lTrozos   := NO
::cRecurso  := ''

if !empty(cComando)
    ::oSocket:SendData( cComando )                          // manda el comando de solicitud...
    inkey(::nDelay)                                         // espera x segundos para dar tiempo a no se que ¿?, pero si no lo pongo, no funciona
endif

if ::lMeter
    @ 0.4, 15 METER oMeter VAR nActual TOTAL 100 of oWnd:oMsgBar SIZE oWnd:nWidth-600, 12 BarColor CLR_HRED, CLR_WHITE
    oMeter:ctext := 'Recibiendo paquetes desde el servidor ...'
    oMeter:lPercentage := NO
endif

do while seconds() < nSegundos

    if ::lMeter
        if ++nActual > 100
            nActual := 0
        endif
        oMeter:Set( nActual )
        if len(aRecurso) = 0
            oMeter:ctext := 'Esperando recibir paquetes HTTP, desde el servidor ...'
          else
            oMeter:ctext := 'Recibidos '+allstr(len(aRecurso))+' paquetes HTTP, desde el servidor de ARG...'
        endif
    endif

    cData := ::oSocket:GetData()
    inkey(::nDelay)                                         // espera 0.05 segundos para dar tiempo a no se que ¿?, pero si no lo pongo, no funciona
*nCanal := fcreate('.\http'+str0s(++nConCanal,3)+'.txt') ; fwrite(nCanal,cData) ; fclose(nCanal)
    if len(cData) > 0   // NO poner "!empty(cData)" porque hay veces que todo el paquete son espacios en blanco
        nSegundos := seconds() + ::nTiempoMax
        if cData = 'HTTP'                                   // si es el primer trozo...
            nPun := at(CRLF+CRLF,cData)                     // ... mira si hay una linea en blanco
            if nPun > 0                                     // si hay una linea en blanco, es que tiene una cabecera...
                ::cCabecera := left(cData,nPun-1)           // ...en cuyo caso, de los datos, separa la cabecera.
                nLineas := mlcount(::cCabecera,254)         // de la cabecera...
                for nL = 1 to nLineas                       // ... analiza todas las lineas de la cabecera
                    cLinea := alltrim(memoline(::cCabecera,254,nL)) // toma una linea concreta
                    cMayus := upper(cLinea)                 // pasa a mayusculas por comodidad de programacion
                    do case
                      case cMayus = 'HTTP/1'                // si es la primera linea...
                        ::h_HTTP    := cLinea               // linea completa
                        ::h_RESN    := val(substr(cLinea,10,3)) // respuesta numerica
                        ::h_RESC    := substr(cLinea,14)    // respuesta en texto
                      case cMayus = 'DATE'
                        ::h_DATE    := cLinea
                      case cMayus = 'SERVER'
                        ::h_SERVER  := cLinea
                      case cMayus = 'LOCATION'
                        ::h_LOCATION:= substr(cLinea,11)
                      case cMayus = 'LAST-MODIFIED'
                        ::h_LAST_MODIFIED   := cLinea
                      case cMayus = 'ETAG'
                        ::h_ETAG    := cLinea
                      case cMayus = 'ACCEPT-RANGES'
                        ::h_ACCEPT_RANGES   := cLinea
                      case cMayus = 'CONTENT-LENGTH'
                        ::h_CONTENT_LENGTH  := cLinea
                        ::nLargo := substr(::h_CONTENT_LENGTH,16)
                        ::nLargo := val(::nLargo)           // la longitud esta en decimal
                        ::lLargo := SI
                      case cMayus = 'TRANSFER-ENCODING'
                        if 'CHUNKED' $ cMayus               // la respuesta esta en trozos
                            ::lTrozos := SI
                        endif
                      case cMayus  = 'CONTENT-TYPE'
                        ::h_CONTENT_TYPE    := cLinea
                    endcase
                next

                ::cDatos := substr(cData,nPun+4)            // del total de datos recibidos, separa el cuerpo

                if ::h_RESN <> 200                          // si NO es 200, algo raro pasa. Dentro del if, analizamos el que. Se pueden poner mas respuestas
                    if ::h_RESN = 301 .or. ::h_RESN = 302
                        return NO
                    endif
                    if ::lAvisa                             // si hay indicacion de avisar...
                        do case
                          case ::h_RESN = 404
                            arginfo('Página no encontrada','Error '+allstr(::h_RESN),'THTTP:228')
                          otherwise
                            arginfo(::h_RESC,'Error '+allstr(::h_RESN),'THTTP:211')
                        endcase
                    endif
                    return NO
                endif

                do case
                  case ::lTrozos                            // si viene en trozos ("chunked"), la primera linea es la longitud del recurso (no del paquete)
                    nPun := at(CRLF,::cDatos)               // busca el primer CRLF
                    ::nLargo := left(::cDatos,nPun-1)       // la primera parte es la longitud en hexadecimal
                    ::nLargo := nHex(::nLargo)              // lo convertimos a decimal
                    ::cDatos := substr(::cDatos,nPun+2)     // el resto son los datos propiamente dichos
                    aadd(aRecurso,::cDatos)                 // pone el primer trozo en el array que formara el recurso recibido
                                                            // vuelve al bucle para seguir recibiendo...

                  case ::lLargo                             // si el recurso esta definido por longitud
                    ::cRecurso := left(::cDatos,::nLargo)
                    ::nTiempo := seconds()-::nTiempo
                    return SI

                  otherwise
                    ::cRecurso := ::cDatos
                    ::nTiempo := seconds()-::nTiempo
                    return SI
                endcase

              else                                          // si NO hay linea en blanco separando la cabecera del recurso...

                ::cCabecera := cData                        // todo es cabecera
                ::cDatos    := ''                           // y por lo tanto, NO hay datos
                ::cRecurso  := ''                           // NI recurso
                ::nTiempo := seconds()-::nTiempo
                return SI

            endif

          else                                              // si NO empieza por HTTP, no es el primer trozo. Es el segundo, tercero, etc.

            do case
              case ::lTrozos                                // si viene en trozos...
                if right(cData,7) = cFinal                  // si contiene el final, es que es el ultimo trozo
                    nLen := len(cData)
                    aadd(aRecurso, left(cData,nLen-7) )     // tomo los bytes
                    nLen := len(aRecurso)
                    for nI = 1 to nLen
                        ::cRecurso += aRecurso[nI]          // forma el recurso a partir del array de trozos recibidos
                    next
                    ::nLargo  := len(::cRecurso)
                    ::nTiempo := seconds()-::nTiempo
                    if ::lMeter
                        fin( @oMeter )
                    endif
                    return SI
                endif
                aadd(aRecurso, cData )                      // tomo los bytes
              case ::lLargo
                ::cRecurso += cData
                if len(::cRecurso) >= ::nLargo
                    return SI
                endif
            endcase
        endif
    endif

enddo

::lTimeOut := SI
if ::lAvisa
    arginfo('Exceso de tiempo ('+allstr(::nTiempoMax)+' seg.) en la respuesta a un comando'+CRLF+CRLF+cComando,'THTTP:298, (solo en ARG)')
endif

return NO

//----------------------------------------------------------------------------//

METHOD End() CLASS THTTP

::oSocket:end()
::oSocket := NIL

return NIL

//----------------------------------------------------------------------------//

METHOD get(cDireccion,cRecurso) CLASS THTTP

local cComando

cComando := 'GET '+cRecurso+' HTTP/1.1'                         +CRLF+; // SOLO el recurso
            'Host: '+cDireccion                                 +CRLF+; // SOLO la direccion
            'Referer: http://'+cDireccion+cRecurso              +CRLF+; // la direccion y el recurso
            'Accept: */*'                                       +CRLF+;
            'Accept-Language: es'                               +CRLF+;
            'Content-Type: application/x-www-form-urlencoded'   +CRLF+; // es necesario para que cPost sea procesado correctamente
            'Content-Length: '+allstr(len(cPost))               +CRLF+; // len(cPost), es el string SOLO, no incluye el CRLF final
            'Connection: close'                                 +CRLF+; // cierra la conexión una vez recibido el recurso
            'Cache-Control: no-cache'                           +CRLF+;
            'User-Agent: '+::cAgente                            +CRLF+; // referencia libre.

::getData(cComando)                             // manda el comando y recibe la respuesta

return NIL

METHOD post(cDireccion,cRecurso,cPost) CLASS THTTP

local cComando

cComando := 'POST '+cRecurso+' HTTP/1.1'                        +CRLF+; // SOLO el recurso
            'Host: '+cDireccion                                 +CRLF+; // SOLO la direccion
            'Referer: http://'+cDireccion+cRecurso              +CRLF+; // la direccion y el recurso
            'Accept: */*'                                       +CRLF+;
            'Accept-Language: es'                               +CRLF+;
            'Content-Type: application/x-www-form-urlencoded'   +CRLF+; // es necesario para que cPost sea procesado correctamente
            'Content-Length: '+allstr(len(cPost))               +CRLF+; // len(cPost), es el string SOLO, no incluye el CRLF final
            'Connection: close'                                 +CRLF+; // cierra la conexión una vez recibido el recurso
            'Cache-Control: no-cache'                           +CRLF+;
            'User-Agent: '+::cAgente                            +CRLF+; // referencia libre.
            ''                                                  +CRLF+; // linea en blanco separando la cabecera del mensaje
            cPost                                               +CRLF

::getData(cComando)                             // manda el comando y recibe la respuesta

return NIL

************************************************************************************************************************
************************************************************************************************************************
************************************************************************************************************************
/*
 /--------------------------------------------------------------------------------------------------------------------\
|   LA MEJOR FORMA DE NO PERDER UNA AYUDA ES INCLUIRLA DENTRO DEL PRG QUE USAMOS, AUNQUE ESTO SUPONGA PEOR ENMAQUETADO |
 \--------------------------------------------------------------------------------------------------------------------/

El protocolo HTTP se puede utilizar para mantener un "diálogo" entre un cliente (nuestro programa) y un servidor, por
ejemplo Apache. La solicitud y posterior envio de páginas WEB es solo una parte de lo que se puede hacer, aunque es la
parte que más se utiliza. Nosotros con esta clase, pretendemos enlazar el cliente y el servidor para sacar todo el
provecho posible.

 Nota:- La función arginfo() es una msginfo() modificada para centrar el dialogo dentro de la ventana padre. Puede ser
    sustituida totalmente por msginfo( <parametros> )

El programa esta basado más en la experiencia que en la teoría. Seguro que es muy mejorable técnicamente, pero a mi
me funciona. Solo quiero compartir para mejorar. Espero contestaciones con preguntas e ideas para completar la clase.

EJEMPLOS:
========
Programa para pedir una actualización. Se indican los parámetros fijos y los variables según el cliente. El servidor
Apache-PHP-SQL comprueba el usuario, la clave, el programa, la versión, etc. y responde en consecuencia además de indicar
los datos FTP para la descarga [ evidentemente, los datos están trucados :-) ] :

        cDireccion  := 'www.la_del_servidor.com'
        nPuerto     := varget('internet.PUERTOSALI',1234)   // varget() es una funcion utilizada en ARG,
        cRecurso:= '/php/valikey.php'

        // estos parámetros pasan como par variable-contenido, y son leidos como tales en el programa PHP del servidor
        cPost   := 'version=110118'                 +;
                   '&buzon='+cBuzon                 +;
                   '&clave='+cPin                   +;
                   '&programa='+cGESxxx+cVersion    +;
                   '&diahora='+cDiaHora             +;
                   '&accion=UPDATE'                 +;
                   '&clavewin='+cClaveWin           +;
                   '&numepan='+cNumePan             +;
                   ''
        oWnd:SetMsg('Esperando contestación de ARG. Espere, por favor.' )

        oHTTP := Thttp():New( cDireccion, nPuerto )                  // prepara los datos, y hace la conexión
        if !oHTTP:lSocket                                            // normalmente NO pasará por aquí.
            arginfo('No se ha creado el socket en la dirección '+cDireccion)
            return NO   // variable del preprocesador que indica .F.
        endif
        oHTTP:nTiempoMax:= varget('internet.TIEMESPE10',10)     // segundos que esperara antes de abandonar la conesión. Por defecto 10.
        oHTTP:nDelay    := varget('internet.INTERPACK',10)/100  // segundos/100 (centésimas de segundo) que espera entre paquetes.
        oHTTP:lMeter    := SI                                   // bandera para sacar un meter en el pie del diálogo.
        oHTTP:lAvisa    := NO                               // bandera de avisar o no al operador del proceso y sus fallos si los hay. Se usa para depuración.
        oHTTP:Post(cDireccion,cRecurso,cPost)               // manda el comando por POST y recibe la respuesta en oHTTP:cRecurso.
                                                            // NO confundir cRecurso (lo que se pide) con oHTTP:cRecurso (lo que se recibe).
        if oHTTP:h_RESN = 200                               // si todo es correcto...
            cRecurso := oHTTP:cRecurso                      // pasamos el recurso a una variable local.
            cRecurso := strtran(cRecurso,'<br>',CRLF)       // _ de línea (interlínea) vienen como <br> que es propio del protocolo HTTP/HTML.
            arglogin(cRecurso)                              // graba un login con lo que se recibe del servidor, que puede estar bien (200) y NO ser lo que se pide.
            memowrit( varget('cDirExe','.')+'\UPDATE.INI', cRecurso)  // graba el fichero recibido como UPDATE.INI para que sirva de control al FTP.
            lPasa := SI
          else
            arglogin(oHTTP:h_RESN+CRLF+oHTTP:cRecurso)
            lPasa := NO
        endif

        oHTTP:end()
        oWnd:SetMsg(varget('messageWnd',' ') )

        if !lPasa
            arginfo(cPost,'Respuesta HTTP: '+allstr(oHTTP:h_RESN)+' No se ha recibido el recurso por el socket '+allstr(oHTTP:nSocket ) )
            return NO
        endif

        etc, etc.

Se pueden hacer peticiones GET, pero no merece la pena, es mejor hacerlo siempre POST. El ejemplo anterior quedaría así (OJO, no lo he probado)

        cDireccion  := 'www.la_del_servidor.com'
        nPuerto     := varget('internet.PUERTOSALI',1234)   // varget() es una funcion utilizada en ARG,
        cRecurso:= '/php/valikey.php'

        // estos parametros pasan como par variable-contenido, y son leidos como tales en el programa PHP del servidor.
        // Aquí cada uno pondría el "dialogo" que quiere mantener con el servidor. Luego hay que programar el servidor para
        // leer las variables y su contenido, actuar en consecuencia y devolver el recurso pedido o el resultado programado.
        cRecurso:= '?'                              +;      // <-- OJO, esto ha cambiado de POST a GET
                   'version=110118'                 +;
                   '&buzon='+cBuzon                 +;
                   '&clave='+cPin                   +;
                   '&programa='+cGESxxx+cVersion    +;
                   '&diahora='+cDiaHora             +;
                   '&accion=UPDATE'                 +;
                   '&clavewin='+cClaveWin           +;
                   '&numepan='+cNumePan             +;
                   ''
        oWnd:SetMsg('Esperando contestación de ARG. Espere, por favor.' )

        oHTTP := Thttp():New( cDireccion, nPuerto )                  // prepara los datos, y hace la conexión
        if !oHTTP:lSocket                                            // normalmente NO pasará por aquí.
            arginfo('No se ha creado el socket en la dirección '+cDireccion)
            return NO   // variable del preprocesador que indica .F.
        endif
        oHTTP:nTiempoMax:= varget('internet.TIEMESPE10',10)     // segundos que esperara antes de abandonar la conesión, en segundos. Por defecto 10
        oHTTP:nDelay    := varget('internet.INTERPACK',10)/100  // segundos/100 (centesimas de segundo) que espera entre paquetes
        oHTTP:lMeter    := SI                                   // bandera para sacar un meter en el pie del diálogo
        oHTTP:lAvisa    := NO                               // bandera de avisar o no al operador del proceso y sus fallos si los hay. Se usa para depuración
        oHTTP:get(cDireccion,cRecurso)       // <-- OJO     // manda el comando por GET y recibe la respuesta en oHTTP:cRecurso
                                                            // NO confundir cRecurso (lo que se pide) con oHTTP:cRecurso (lo que se recibe)
        if oHTTP:h_RESN = 200                               // si todo es correcto...
            cRecurso := oHTTP:cRecurso                      // pasamos el recurso a una variable local
            cRecurso := strtran(cRecurso,'<br>',CRLF)       // _ de linea vienen como <br> que es propio del protocolo HTTP/HTML
            arglogin(cRecurso)                              // graba un login con lo que se recibe del servidor, que puede estar bien y NO ser lo que se pide
            memowrit( varget('cDirExe','.')+'\UPDATE.INI', cRecurso)  // graba el fichero recibido como UPDATE.INI para que sirva de control al FTP
            lPasa := SI
          else
            arglogin(oHTTP:h_RESN+CRLF+oHTTP:cRecurso)
            lPasa := NO
        endif

        oHTTP:end()
        oWnd:SetMsg(varget('messageWnd',' ') )

        if !lPasa
            arginfo(cPost,'Respuesta HTTP: '+allstr(oHTTP:h_RESN)+' No se ha recibido el recurso por el socket '+allstr(oHTTP:nSocket ) )
            return NO
        endif

        etc, etc.

Este es otro ejemplo, Aqui vemos como se hace una petición desde un programa clipper a un servidor de mensajes SMS para móviles:
El servidor, que no es de ARG,  NECESITA que sea GET

        cDireccion  := '84.124.123.321'                     // dirección del servidor. Aqui es ficticia
        cRecurso    := '/pls/sms/sms.CGIEnviaSMS'           // NO importa el orden de los siguientes parámetros
        cRecurso    += '?pusuario='     + cUsuario          // fijarse en el signo "?" que hay que poner antes del primer parámetro
        cRecurso    += '&pclave='       + cClave
        cRecurso    += '&pdestino=34'   + alltrim(aMovil[1])
        cRecurso    += '&ptexto='       + db2ht(alltrim(cTexto))    // db2ht() es una función que convierte una cadena clipper en una HTTP
        cRecurso    += '&porigen='      + db2ht(cOrigen)
        cRecurso    += '&psmsid='       + db2ht(time())
        cRecurso    += '&pflash='       + 'N'
    if lConfirma
        cRecurso    += '&pacuse='       + cCorreo
    endif
        oHTTP := Thttp():New( cDireccion )                      // prepara los datos, y hace la conexión
        oHTTP:nTiempoMax:= varget('internet.TIEMESPE5',5)       // segundos que esperara antes de abandonar la conesión
        oHTTP:lAvisa    := NO
        oHTTP:get(cDireccion,cRecurso)           // <-- OJO     // manda el comando por GET y recibe la respuesta en oHTTP:cRecurso

        cRes    := oHTTP:h_RESC
        nRes    := oHTTP:h_RESN
        cRecurso:= oHTTP:cRecurso

        oHTTP:end()

        msgwait('Enviado mensaje '+alltrim(aMovil[1]),'Envio SMS',1)

        if cRecurso = 'OK'
            arginfo(strtoken(cRecurso,2,'#')+'.'                            +CRLF+;
                    'Número: '+alltrim(aMovil[1])                           +CRLF+;
                    'Referencia: '+strtoken(cRecurso,3,'#')                 +CRLF+;
                    'Créditos consumidos: '+strtoken(cRecurso,4,'#')        +CRLF+;
                    'Créditos ACTUALES:   '  +strtoken(cRecurso,5,'#') )
            nSaldo := val(strtoken(cRecurso,5,'#'))
          else
            arginfo('Respuesta:'+allstr(nRes)+'.-'+cRes+CRLF+;
                'Recurso:  '+cRecurso)
            nSaldo := -1
            return NO
        endif

Hay que tener en cuenta el conjunto de caracteres, que en HTTP NO se usa el juego de windows, pero eso es otro tema.

*/
 
Bien, lo conseguí :D

Los que empeceís en programación, no ver mi forma de programar como un ejemplo. Los que sabeís mas que yo (casi todos) ser comprensivos y perdonarme.
Admito críticas :oops:

NOTA IMPORTANTE: Para los que quieran saber más sobre el tema del protocolo HTTP buscar en internet el documento RFC 2616. Es un documento de casi 200 páginas en inglés perfectamente ilegible :cry:
Post Reply