Autor Tema: clsHttpRequests: Clase que envia Requests de forma asincronica con threads (r6)  (Leído 11027 veces)

0 Usuarios y 1 Visitante están viendo este tema.

coco

  • Administrador
  • Terabyte
  • *****
  • Mensajes: 548
  • Reputación: +63/-3
    • Ver Perfil
Buenas a todos!

version: REVISION 6 (21/febrero/14)!!!!

El martes me surgio la idea de enviar un HTTP POST de forma asincronica, y no encontre nada piola.
Hablando con Cobein se me ocurrio el de utilizar un codigo que use el HttpCreateRequest y HttpSendRequest, pero que la llamada bloqueante (HttpSendRequest) se haga en un thread aparte.
Ahora, la magia reside en que la parte "heavy" se hace en VB, mientras que en el thread solo se hace la llamada a HttpSendRequest y a SendMessageTimeout (para avisarle al thread 'main' que ya termino la ejecucion).
El codigo del thread esta hecho 100% en assembly (ver asm.asm). En dicho archivo, se ven unos pushs y unos llamados a cualquier lugar (fuera de joda, lo hice aproposito). La gracia de eso, es que el codigo en VB va a parchear eso, con llamadas a direcciones validas (la del HttpSendRequest y SendMessageTimeout), como asi tambien, reemplazar las "constantes" del hWnd de la ventana, y el uMsg custom. (la clase crea una ventana oculta para recibir los mensajes del thread, y el mensaje es uno custom, creado con RegisterWindowMessage).

La logica es sencilla. Se llama a la funcion SendRequest o SendRequest_OptionalAsByte con todos los parametros, inclusive el "private key", que seria un string para identificar los eventos (la diferencia radica en que la primera, el parametro opcional lpOptional es interpretado en base a un string, y en la segunda, es un array de bytes y precisa que se le pase el tamaño del mismo).
Tambien hay un parametro de Timeout.

Los demas parametros conforman a los que uno usaria al llamar a InternetConnect y HttpCreateRequest.

Hay varias enums de flags y demas, para usar en esas llamadas, o en QueryInfoAsXXXXX (puede ser AsLong, AsDate, AsString).

QueryInfoAsXXXX consulta algun parametro dentro del header y lo devuelve, segun el formato elegido (leer el msdn para saber si usar Long, Date o String). La funcion acomoda el tamaño del string automaticamente.

Luego quedan las funciones DumpRequestToBuffer, DumpRequestToString y DumpRequestToFile (todas estas leen el resultado del Request, y lo devuelven de maneras distintas).

Tambien hay una funcion mas, CancelRequest, la cual se podria llamar para cancelar un Request que se esta ejecutando.

Eventos: RequestDone (cuando HttpSendRequest termina), RequestCancelled (cuando es cancelado por el usuario), RequestTimeout (cuando hay timeout)

En el ejemplo hay 3 botones.
1º Descarga el google a googlex.html
2º Descarga 10 veces el google a google_xx.html
3º Envia un POST a un php en un host mio, que solamente devuelve el parametro "q"



  • rev1: Ahora la cantidad maxima de request esta limitada (para prevenir arrays locks); Se volo a la mierda el PostMessage y se reemplazo por el
    SendMessageTimeout (espera hasta que el thread main le responda); Se corrigieron unos leaks de handles (faltaba cerrar el hRequest)
  • rev2: Se corrigio el leak de handles (no se cerraba el objeto thread)
  • rev3: Se agrego la opcion de "Esperar a que terminen todos los threads". Esto es importante, porque antes se deallocaba la memoria de los threads, y si estos aun estaban activos, iba a surgir un error de "Memory access violation". Ademas le agregue el evento del WM_TIMER, que me lo habia olvidado y no estaba funcionando la parte del timeout.
  • rev4: Se agrego el soporte para SSL y Proxy. Se removieron muchos parametros y ahora solo se pasa una URL. Ahora en los eventos aparece el LastError del thread, el cual indica porque fallo el API. El "esperar a que los threads terminen" es una propiedad que puede ser cambiada en runtime (en IDE es 100% necesario!!!).
  • rev5: Se arreglo DumpRequestToString y DumpRequestToBufer, el cual escribia en un pedazo de memoria que no correspondia.
  • rev6: Se modificaron algunos eventos y propiedades para que el control se parezca mas al WinHTTP. Se agregó toda la logica de InternetReadFile en el thread, de forma que el thread de VB no se bloquea mas (ya que InternetReadFile bloquea).

(el link siempre es el mismo para LA ULTIMA VERSION)
DESCARGAR rev6

Cualquier cosa los escucho...
Saludos!!!
« última modificación: Marzo 23, 2014, 04:52:24 pm por coco »
'-     coco
(No me cabe: Java, Python ni Pascal)
SQLite - PIC 16F y 18F - ARM STM32 - ESP32 - Linux Embebido - VB6 - Electronica - Sonido y Ambientacion

LeandroA

  • Administrador
  • Petabyte
  • *****
  • Mensajes: 1128
  • Reputación: +151/-8
    • Ver Perfil
buena coco, hace rato que vengo buscando una clase asi pero nunca encontré nada, lo unico mas cercano fue usando createobject() y metiendo un timer para verificar si había terminado, u otras chanchadas como crear otro exe para hacer peticiones.

Saludos

coco

  • Administrador
  • Terabyte
  • *****
  • Mensajes: 548
  • Reputación: +63/-3
    • Ver Perfil
Si, yo tambien estaba en la misma... Lo hice lo mas generico posible (un ejemplo es si queres enviar una String o un array de bytes). Tambien le hice pruebas "destructivas", y no vi nada raro. Lo unico que es ESPERABLE, es que si estas en el IDE y crashea, empieces a leakear memoria (tanto de las structs, como de la memoria donde esta el code en assembly), pero esto pasa hasta con el self subclass, y no habria que ni preocuparse (en el .EXE de 'produccion' no ocurre esto).

Cualquier problema, o feature request, comenten!!! Quiero saber si sirvio de algo ajjaja
'-     coco
(No me cabe: Java, Python ni Pascal)
SQLite - PIC 16F y 18F - ARM STM32 - ESP32 - Linux Embebido - VB6 - Electronica - Sonido y Ambientacion

cobein

  • Moderador Global
  • Gigabyte
  • *****
  • Mensajes: 348
  • Reputación: +63/-0
  • Más Argentino que el morcipan
    • Ver Perfil
Genial coco, la verdad quedo re solido :).

coco

  • Administrador
  • Terabyte
  • *****
  • Mensajes: 548
  • Reputación: +63/-3
    • Ver Perfil
Revision 1: (19/jul/13)

En vista de algunas inquietudes del pelado Cobein, cambie la forma en la que se agregan los Request, dejando el array con un tamaño fijo (se puede incrementar el maximo, pero me parece una locura usar mas de 200 request a la vez).
Tambien se me ocurrio de cambiar el PostMessage por algo mas solido, y ahora usa SendMessageTimeout, el cual hace que cada thread ESPERE a que el thread main "lea" el mensaje enviado.
Y por ultimo, encontre un par de forraditas que leakeaban memoria (me olvide de cerrar el request).
'-     coco
(No me cabe: Java, Python ni Pascal)
SQLite - PIC 16F y 18F - ARM STM32 - ESP32 - Linux Embebido - VB6 - Electronica - Sonido y Ambientacion

coco

  • Administrador
  • Terabyte
  • *****
  • Mensajes: 548
  • Reputación: +63/-3
    • Ver Perfil
Revision 2: (19/jul/13)

Arreglado el leakeage de Handles, porque al enviar un mensaje desde el thread, no se podia matarlo, asi que es mas sencillo cerrar el handle y olvidarse.
'-     coco
(No me cabe: Java, Python ni Pascal)
SQLite - PIC 16F y 18F - ARM STM32 - ESP32 - Linux Embebido - VB6 - Electronica - Sonido y Ambientacion

cobein

  • Moderador Global
  • Gigabyte
  • *****
  • Mensajes: 348
  • Reputación: +63/-0
  • Más Argentino que el morcipan
    • Ver Perfil
Perfecto coco, lo probe y esta andando de 10 ahora. Cualquier cosa te aviso.

E N T E R

  • Petabyte
  • ******
  • Mensajes: 1062
  • Reputación: +57/-13
  • www.enterpy.com
    • Ver Perfil
    • www.enterpy.com
Disculpen, baje y probé el proyecto pero para que mas o menos me serviría esto?

Saludos...
CIBER GOOGLE - CONCEPCIÓN PARAGUAY
www.enterpy.com
Primera regla de la programacion, para que vas a hacerlo complicado si lo puedes hacer sencillo

cobein

  • Moderador Global
  • Gigabyte
  • *****
  • Mensajes: 348
  • Reputación: +63/-0
  • Más Argentino que el morcipan
    • Ver Perfil
Para hacer HTTP requests, podes por ejemplo descargar una pagina/documento, subir informacion a traves de get/post o lo que quieras de manera asincronica (no bloquea) por ende podes como en el ejemplo, descargar 10 paginas a la vez sin bloquear la interfaz.

coco

  • Administrador
  • Terabyte
  • *****
  • Mensajes: 548
  • Reputación: +63/-3
    • Ver Perfil
Si, es como dice Cobein. Yo lo habia hecho para hacer una especie de widget sobre "cuando llega" (un servicio que te indica cuanto falta para que un colectivo/bus llegue a una parada).

Lo que vos podes hacer con esto, es muy similar a usar el objeto XMLHTTP:
Código: (VB) [Seleccionar]
        Set objXML = CreateObject("Microsoft.XMLHTTP")

        With objXML
                .open "GET", sURL, True
                .send

                If .readyState = 0 Then
                        Debug.Print "Error!"
                        Exit Sub
                End If
        End With

        tmrWait.Enabled = True

'// y esto iria en el sub del timer, para chequear el estado del Request
        If objXML.readyState = 4 Then
                tmrWait.Enabled = False
                '// leer la data
                '// ...
        End If

Que seguramente lo viste en varios proyectos de Leandro (inclusive, en el del Facebook, que creaba varios .exe para cada 'request'; de esa forma la interfaz no se bloquea nunca)

En resumen, vos podes enviar un GET o POST a algun sitio (en realidad es mas flexible, y soporta mas metodos), y esperar que el mismo te responda, y leer lo que devuelve.
Esto sirve para hacer procedimientos remotos a algun PHP, o CGI. Por ejemplo uno puede hacer una interfaz en VB de algun sitio que use AJAX, usando 'mas o menos' la misma logica que el Javascript, utilizando este clase como el XMLHTTP.

Saludos
'-     coco
(No me cabe: Java, Python ni Pascal)
SQLite - PIC 16F y 18F - ARM STM32 - ESP32 - Linux Embebido - VB6 - Electronica - Sonido y Ambientacion

coco

  • Administrador
  • Terabyte
  • *****
  • Mensajes: 548
  • Reputación: +63/-3
    • Ver Perfil
Nueva version! Se agrego la posibilidad de "esperar" a que terminen todos los threads. Esto haria que no haya problemas del tipo "Memory access violation", cuando uno borraba la clase y aun existian threads funcionando.

El codigo que espera a que terminen todos es muy rapido (ya que al cerrar un handle, el API del thread deberia retornar inmediatamente) y no tilda la aplicacion.
'-     coco
(No me cabe: Java, Python ni Pascal)
SQLite - PIC 16F y 18F - ARM STM32 - ESP32 - Linux Embebido - VB6 - Electronica - Sonido y Ambientacion

cliv

  • Kilobyte
  • **
  • Mensajes: 69
  • Reputación: +1/-2
    • Ver Perfil
I use Microsoft internet transfer control and my code ... and work
Código: [Seleccionar]
  Dim gbUrl As String
  Dim gbParam As String
  gbUrl = "http://localhost/termo/inetgb-CSV.php"
  gbParam = "insert=no&sql=" & txtSQL.Text
  InetGb.Execute gbUrl, "POST", gbParam, "Content-Type: application/x-www-form-urlencoded"

I use your class with this code ... and not work . What i'm doing wrong?
Código: [Seleccionar]
  Dim gbParam As String
  gbParam = "insert=no&sql=" & txtSQL.Text
  Call obj.SendRequest("http://localhost/termo", "/inetgb-CSV.php", "Date.csv", _
                        sAdditionalHeaders:="Content-Type: application/x-www-form-urlencoded", _
                        sOptionalData:=gbParam, sVerb:="POST")

Código: [Seleccionar]
txtSQL.Text="SELECT * FROM inetgb WHERE ID>3"
« última modificación: Febrero 10, 2014, 06:05:01 am por cliv »

coco

  • Administrador
  • Terabyte
  • *****
  • Mensajes: 548
  • Reputación: +63/-3
    • Ver Perfil
@cliv, you're not passing correctly the aprameters.
The first one is just the "host", you don't have to add any URI in there.
For the second parameter, go ahead and add all the URI there.
Also, "Date.csv" is just a "key", it won't save any file. You just have to handle the event RequestDone and read the file with DumpRquestToString


I bet your code should be like this:
Código: (vb) [Seleccionar]
  Dim gbParam As String
  gbParam = "insert=no&sql=" & txtSQL.Text
  Call obj.SendRequest("localhost", "/termo/inetgb-CSV.php", "SomeKeyToIdentifyThisRequest", _
                        sAdditionalHeaders:="Content-Type: application/x-www-form-urlencoded", _
                        sOptionalData:=gbParam, sVerb:="POST")

And for the request reading:
Código: (vb) [Seleccionar]
Private Sub obj_RequestDone(ByVal sPrivateKey As String, ByVal lReturned As Long, ByVal lhRequest As Long)
    Dim sAA As String
    Call obj.DumpRequestToString(lhRequest, sAA)
    Debug.Print "Request '"; sPrivateKey; "', returned: "; sAA
End Sub

Tellme if that works.
'-     coco
(No me cabe: Java, Python ni Pascal)
SQLite - PIC 16F y 18F - ARM STM32 - ESP32 - Linux Embebido - VB6 - Electronica - Sonido y Ambientacion

coco

  • Administrador
  • Terabyte
  • *****
  • Mensajes: 548
  • Reputación: +63/-3
    • Ver Perfil
Revision 5 recien salida del horno!
Agrega SSL y Proxy, como asi tambien corrige un monton de errores que he encontrado.
Tambien agrega la posibilidad de saber porque no se completo un Request (GetLastError() del thread).

Saludos!
'-     coco
(No me cabe: Java, Python ni Pascal)
SQLite - PIC 16F y 18F - ARM STM32 - ESP32 - Linux Embebido - VB6 - Electronica - Sonido y Ambientacion

coco

  • Administrador
  • Terabyte
  • *****
  • Mensajes: 548
  • Reputación: +63/-3
    • Ver Perfil
Revision 6 con features:
Se metio la logica de InternetReadFile en el thread (ya que esa api bloquea), y se modificaron algunos eventos y propiedades para hacer parecer este objeto al control WinHTTP.
'-     coco
(No me cabe: Java, Python ni Pascal)
SQLite - PIC 16F y 18F - ARM STM32 - ESP32 - Linux Embebido - VB6 - Electronica - Sonido y Ambientacion