rts-curl.c

     
   1  //! @file rts-curl.c
   2  //! @author J. Marcel van der Veer
   3  
   4  //! @section Copyright
   5  //!
   6  //! This file is part of Algol68G - an Algol 68 compiler-interpreter.
   7  //! Copyright 2001-2025 J. Marcel van der Veer [algol68g@xs4all.nl].
   8  
   9  //! @section License
  10  //!
  11  //! This program is free software; you can redistribute it and/or modify it 
  12  //! under the terms of the GNU General Public License as published by the 
  13  //! Free Software Foundation; either version 3 of the License, or 
  14  //! (at your option) any later version.
  15  //!
  16  //! This program is distributed in the hope that it will be useful, but 
  17  //! WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  18  //! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 
  19  //! more details. You should have received a copy of the GNU General Public 
  20  //! License along with this program. If not, see [http://www.gnu.org/licenses/].
  21  
  22  //! @section Synopsis
  23  //!
  24  //! HTTP/HTTPS client.
  25  
  26  #include "a68g.h"
  27  #include "a68g-genie.h"
  28  #include "a68g-prelude.h"
  29  #include "a68g-transput.h"
  30  
  31  #if defined (HAVE_CURL)
  32  
  33  #if defined (HAVE_CURL_CURL_H)
  34    #include <curl/curl.h>
  35  #endif
  36  
  37  typedef struct DATA_T DATA_T;
  38  struct DATA_T {
  39    char *ref;
  40    size_t len;
  41  };
  42  
  43  #define NO_DATA ((DATA_T *) NULL)
  44  
  45  // Below, 0 means default, which are 300 s / endless respectively.
  46  
  47  static INT_T a68g_curl_connecttimeout = 0, a68g_curl_timeout = 0;
  48  
  49  // Callback function concatenating received data.
  50  
  51  static size_t a68g_curl_concat (void *data, size_t len, size_t n, void *buf)
  52  {
  53  // Sanity checks.
  54    if ((char *) data == NO_TEXT || (DATA_T *) buf == NO_DATA) {
  55      return 0;
  56    } else if (n == 0 || len == 0) {
  57      return 0;
  58    }
  59    ABEND (len >= (2 * A68G_GIGA) / n, ERROR_MEMORY_FULL, NO_TEXT);
  60    size_t new_len = n * len;
  61    ABEND (new_len + 1 > 2 * A68G_GIGA - ((DATA_T *) buf)->len, ERROR_MEMORY_FULL, NO_TEXT);
  62    char *stale = ((DATA_T *) buf)->ref;
  63    ((DATA_T *) buf)->ref = malloc (((DATA_T *) buf)->len + new_len + 1);
  64    ABEND (((DATA_T *) buf)->ref == NO_TEXT, ERROR_MEMORY_FULL, NO_TEXT);
  65    if (stale != NO_TEXT) {
  66      MOVE (((DATA_T *) buf)->ref, stale, ((DATA_T *) buf)->len + 1);
  67      a68g_free (stale);
  68    }
  69    MOVE (& (((DATA_T *) buf)->ref[((DATA_T *) buf)->len]), data, new_len);
  70    ((DATA_T *) buf)->len += new_len;
  71    ((DATA_T *) buf)->ref[((DATA_T *) buf)->len] = NULL_CHAR;
  72    return new_len;
  73  }
  74  
  75  void genie_curl_content (NODE_T * p, char *protocol)
  76  {
  77    A68G_REF path_string, domain_string, content_string;
  78    A68G_INT port;
  79    errno = 0;
  80  // Pop arguments.
  81    POP_OBJECT (p, &port, A68G_INT);
  82    CHECK_INIT (p, INITIALISED (&port), M_INT); // Unused for now.
  83    POP_REF (p, &path_string);
  84    CHECK_INIT (p, INITIALISED (&path_string), M_STRING);
  85    POP_REF (p, &domain_string);
  86    CHECK_INIT (p, INITIALISED (&domain_string), M_STRING);
  87    POP_REF (p, &content_string);
  88    CHECK_REF (p, content_string, M_REF_STRING);
  89    *DEREF (A68G_REF, &content_string) = empty_string (p);
  90  // Set buffers.
  91    reset_transput_buffer (DOMAIN_BUFFER);
  92    add_a_string_transput_buffer (p, DOMAIN_BUFFER, (BYTE_T *) & domain_string);
  93    reset_transput_buffer (PATH_BUFFER);
  94    add_a_string_transput_buffer (p, PATH_BUFFER, (BYTE_T *) & path_string);
  95  // Compose request.
  96    reset_transput_buffer (REQUEST_BUFFER);
  97    if (protocol != NO_TEXT) {
  98      add_string_transput_buffer (p, REQUEST_BUFFER, protocol);
  99    }
 100    add_string_transput_buffer (p, REQUEST_BUFFER, get_transput_buffer (DOMAIN_BUFFER));
 101    add_string_transput_buffer (p, REQUEST_BUFFER, get_transput_buffer (PATH_BUFFER));
 102  // cURL connects to host, negotiates and collects data.
 103    DATA_T data = {NO_TEXT, 0};
 104    curl_global_init (CURL_GLOBAL_ALL);
 105    CURL *handle = curl_easy_init ();
 106    curl_easy_setopt (handle, CURLOPT_URL, get_transput_buffer (REQUEST_BUFFER));
 107    curl_easy_setopt (handle, CURLOPT_WRITEFUNCTION, a68g_curl_concat);
 108    curl_easy_setopt (handle, CURLOPT_WRITEDATA, (void *) &data);
 109    curl_easy_setopt (handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
 110    curl_easy_setopt (handle, CURLOPT_CONNECTTIMEOUT, (long) a68g_curl_connecttimeout);
 111    curl_easy_setopt (handle, CURLOPT_TIMEOUT, (long) a68g_curl_timeout);
 112    CURLcode rc = curl_easy_perform (handle);
 113  // Wrap it up.
 114    if (rc != CURLE_OK) {
 115      errno = rc;
 116    } else {
 117      *DEREF (A68G_REF, &content_string) = c_to_a_string (p, data.ref, data.len);
 118    }
 119    if (data.ref != NO_TEXT) {
 120      a68g_free (data.ref);
 121    }
 122    curl_easy_cleanup (handle);
 123    curl_global_cleanup ();
 124    PUSH_VALUE (p, errno, A68G_INT);
 125  }
 126  
 127  //! @brief PROC (INT, INT) VOID http timeout
 128  
 129  void genie_curl_timeout (NODE_T *p)
 130  {
 131    A68G_INT i, j;
 132    POP_OBJECT (p, &j, A68G_INT);
 133    POP_OBJECT (p, &i, A68G_INT);
 134    a68g_curl_connecttimeout = VALUE (&i);
 135    a68g_curl_timeout = VALUE (&j);
 136  }
 137  
 138  //! @brief PROC (REF STRING, STRING, STRING, INT) INT http content 
 139  
 140  void genie_http_content (NODE_T * p)
 141  {
 142     genie_curl_content (p, "http://");
 143  }
 144  
 145  //! @brief PROC (REF STRING, STRING, STRING, INT) INT https content 
 146  
 147  void genie_https_content (NODE_T * p)
 148  {
 149     genie_curl_content (p, "https://");
 150  }
 151  
 152  #endif
     


© 2002-2025 J.M. van der Veer (jmvdveer@xs4all.nl)