Example #1
0
// Set the options on the Curl handle from a Request object. Takes each field
// in the Request object and uses it to set the appropriate option on the Curl
// handle.
static void set_options_from_request(VALUE self, VALUE request) {
  struct curl_state *state;
  Data_Get_Struct(self, struct curl_state, state);

  CURL* curl = state->handle;

  VALUE headers = rb_iv_get(request, "@headers");
  if (!NIL_P(headers)) {
    if (rb_type(headers) != T_HASH) {
      rb_raise(rb_eArgError, "Headers must be passed in a hash.");
    }

    rb_iterate(rb_each, headers, each_http_header, self);
  }

  ID action = SYM2ID(rb_iv_get(request, "@action"));
  if (action == rb_intern("get")) {
    curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);

    VALUE download_file = rb_iv_get(request, "@file_name");
    if (!NIL_P(download_file)) {
      state->download_file = open_file(download_file, "w");
      curl_easy_setopt(curl, CURLOPT_WRITEDATA, state->download_file);
    } else {
      state->download_file = NULL;
    }
  } else if (action == rb_intern("post") || action == rb_intern("put")) {
    VALUE data = rb_iv_get(request, "@upload_data");
    VALUE filename = rb_iv_get(request, "@file_name");

    if (!NIL_P(data)) {
      state->upload_buf = StringValuePtr(data);
      int len = RSTRING_LEN(data);

      if (action == rb_intern("post")) {
        curl_easy_setopt(curl, CURLOPT_POST, 1);
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, state->upload_buf);
        curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, len);
      } else {
        curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
        curl_easy_setopt(curl, CURLOPT_READFUNCTION, &session_read_handler);
        curl_easy_setopt(curl, CURLOPT_READDATA, &state->upload_buf);
        curl_easy_setopt(curl, CURLOPT_INFILESIZE, len);
      }
    } else if (!NIL_P(filename)) {
      set_chunked_encoding(state);

      curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);

      if (action == rb_intern("post")) {
        curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
      }

      state->upload_file = open_file(filename, "r");
      curl_easy_setopt(curl, CURLOPT_READDATA, state->upload_file);
    } else {
      rb_raise(rb_eArgError, "Must provide either data or a filename when doing a PUT or POST");
    }
  } else if (action == rb_intern("head")) {
    curl_easy_setopt(curl, CURLOPT_NOBODY, 1);
  } else {
    VALUE action_name = rb_funcall(request, rb_intern("action_name"), 0);
    curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, StringValuePtr(action_name));
  }

  curl_easy_setopt(curl, CURLOPT_HTTPHEADER, state->headers);
  curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, state->error_buf);

  VALUE url = rb_iv_get(request, "@url");
  if (NIL_P(url)) {
    rb_raise(rb_eArgError, "Must provide a URL");
  }
  curl_easy_setopt(curl, CURLOPT_URL, StringValuePtr(url));

  VALUE timeout = rb_iv_get(request, "@timeout");
  if (!NIL_P(timeout)) {
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, FIX2INT(timeout));
  }

  timeout = rb_iv_get(request, "@connect_timeout");
  if (!NIL_P(timeout)) {
    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, FIX2INT(timeout));
  }

  VALUE redirects = rb_iv_get(request, "@max_redirects");
  if (!NIL_P(redirects)) {
    int r = FIX2INT(redirects);
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, r == 0 ? 0 : 1);
    curl_easy_setopt(curl, CURLOPT_MAXREDIRS, r);
  }

  VALUE proxy = rb_iv_get(request, "@proxy");
  if (!NIL_P(proxy)) {
      curl_easy_setopt(curl, CURLOPT_PROXY, StringValuePtr(proxy));
  }

  VALUE credentials = rb_funcall(request, rb_intern("credentials"), 0);
  if (!NIL_P(credentials)) {
    curl_easy_setopt(curl, CURLOPT_HTTPAUTH, rb_iv_get(request, "@auth_type"));
    curl_easy_setopt(curl, CURLOPT_USERPWD, StringValuePtr(credentials));
  }

  VALUE insecure = rb_iv_get(request, "@insecure");
  if(!NIL_P(insecure)) {
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1);
  }
}
Example #2
0
/* Set the options on the Curl handle from a Request object. Takes each field
 * in the Request object and uses it to set the appropriate option on the Curl
 * handle.
 */
static void set_options_from_request(VALUE self, VALUE request) {
  struct curl_state* state = get_curl_state(self);
  CURL* curl = state->handle;

  ID    action                = Qnil;
  VALUE headers               = Qnil;
  VALUE url                   = Qnil;
  VALUE timeout               = Qnil;
  VALUE redirects             = Qnil;
  VALUE proxy                 = Qnil;
  VALUE proxy_type            = Qnil;
  VALUE credentials           = Qnil;
  VALUE ignore_content_length = Qnil;
  VALUE insecure              = Qnil;
  VALUE cacert                = Qnil;
  VALUE ssl_version           = Qnil;
  VALUE buffer_size           = Qnil;
  VALUE action_name           = rb_funcall(request, rb_intern("action"), 0);
  VALUE a_c_encoding          = rb_funcall(request, rb_intern("automatic_content_encoding"), 0);

  headers = rb_funcall(request, rb_intern("headers"), 0);
  if (RTEST(headers)) {
    if (rb_type(headers) != T_HASH) {
      rb_raise(rb_eArgError, "Headers must be passed in a hash.");
    }

    rb_hash_foreach(headers, each_http_header, self);
  }

  action = SYM2ID(action_name);
  if(rb_funcall(request, rb_intern("force_ipv4"), 0)) {
    curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
  }
  if (action == rb_intern("get")) {
    VALUE data = rb_funcall(request, rb_intern("upload_data"), 0);
    VALUE download_file = rb_funcall(request, rb_intern("file_name"), 0);

    curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
    if (RTEST(data)) {
      set_request_body(state, data);
      curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET");
    }
    if (RTEST(download_file)) {
      state->download_file = open_file(download_file, "wb");
      curl_easy_setopt(curl, CURLOPT_WRITEDATA, state->download_file);
    } else {
      state->download_file = NULL;
    }
  } else if (action == rb_intern("post") || action == rb_intern("put") || action == rb_intern("patch")) {
    VALUE data = rb_funcall(request, rb_intern("upload_data"), 0);
    VALUE filename = rb_funcall(request, rb_intern("file_name"), 0);
    VALUE multipart = rb_funcall(request, rb_intern("multipart"), 0);

    if (action == rb_intern("post")) {
      curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
    } else if (action == rb_intern("put")) {
      curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
    } else if (action == rb_intern("patch")) {
      curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH");
    }
    
    if (RTEST(data) && !RTEST(multipart)) {
      if (action == rb_intern("post")) {
        curl_easy_setopt(curl, CURLOPT_POST, 1);
      }
      set_request_body(state, data);
    } else if (RTEST(filename) && !RTEST(multipart)) {
      set_chunked_encoding(state);
      set_request_body_file(state, filename);
    } else if (RTEST(multipart)) {
      if (action == rb_intern("post")) {
        if(RTEST(data) && RTEST(filename)) {
          if (rb_type(data) == T_HASH && rb_type(filename) == T_HASH) {
            rb_hash_foreach(data, formadd_values, self);
            rb_hash_foreach(filename, formadd_files, self);
          } else {
            rb_raise(rb_eArgError, "Data and Filename must be passed in a hash.");
          }
        }
        curl_easy_setopt(curl, CURLOPT_HTTPPOST, state->post);
      } else {
         rb_raise(rb_eArgError, "Multipart PUT not supported");
      }

    } else {
      rb_raise(rb_eArgError, "Must provide either data or a filename when doing a PUT or POST");
    }

  // support for data passed with a DELETE request (e.g.: used by elasticsearch)
  } else if (action == rb_intern("delete")) {
      VALUE data = rb_funcall(request, rb_intern("upload_data"), 0);
      if (RTEST(data)) {
        long len = RSTRING_LEN(data);
        state->upload_buf = StringValuePtr(data);
        curl_easy_setopt(curl, CURLOPT_POST, 1);
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, state->upload_buf);
        curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, len);
      }
      curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");

  } else if (action == rb_intern("head")) {
    curl_easy_setopt(curl, CURLOPT_NOBODY, 1);
  } else {
    VALUE action_name = rb_funcall(request, rb_intern("action_name"), 0);
    curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, StringValuePtr(action_name));
  }

  curl_easy_setopt(curl, CURLOPT_HTTPHEADER, state->headers);
  curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, state->error_buf);

  // Enable automatic content-encoding support via gzip/deflate if set in the request,
  // see https://curl.haxx.se/libcurl/c/CURLOPT_ACCEPT_ENCODING.html
  if(RTEST(a_c_encoding)) {
    #ifdef CURLOPT_ACCEPT_ENCODING
      curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");
    #elif defined CURLOPT_ENCODING
      curl_easy_setopt(curl, CURLOPT_ENCODING, "");
    #else
      rb_raise(rb_eArgError,
        "The libcurl version installed doesn't support automatic content negotiation");
    #endif
  }
  
  url = rb_funcall(request, rb_intern("url"), 0);
  if (!RTEST(url)) {
    rb_raise(rb_eArgError, "Must provide a URL");
  }
  curl_easy_setopt(curl, CURLOPT_URL, StringValuePtr(url));
  
#ifdef CURLPROTO_HTTP
  // Security: do not allow Curl to go looking on gopher/SMTP etc.
  // Must prevent situations like this:
  // https://hackerone.com/reports/115748
  curl_easy_setopt(curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
  curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
#endif
    
  timeout = rb_funcall(request, rb_intern("timeout"), 0);
  if (RTEST(timeout)) {
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, FIX2INT(timeout));
  }

  timeout = rb_funcall(request, rb_intern("connect_timeout"), 0);
  if (RTEST(timeout)) {
    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, FIX2INT(timeout));
  }

  redirects = rb_funcall(request, rb_intern("max_redirects"), 0);
  if (RTEST(redirects)) {
    int r = FIX2INT(redirects);
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, r == 0 ? 0 : 1);
    curl_easy_setopt(curl, CURLOPT_MAXREDIRS, r);
  }

  proxy = rb_funcall(request, rb_intern("proxy"), 0);
  if (RTEST(proxy)) {
    curl_easy_setopt(curl, CURLOPT_PROXY, StringValuePtr(proxy));
  }

  proxy_type = rb_funcall(request, rb_intern("proxy_type"), 0);
  if (RTEST(proxy_type)) {
    curl_easy_setopt(curl, CURLOPT_PROXYTYPE, NUM2LONG(proxy_type));
  }

  credentials = rb_funcall(request, rb_intern("credentials"), 0);
  if (RTEST(credentials)) {
    VALUE auth_type = rb_funcall(request, rb_intern("auth_type"), 0);
    curl_easy_setopt(curl, CURLOPT_HTTPAUTH, NUM2LONG(auth_type));
    curl_easy_setopt(curl, CURLOPT_USERPWD, StringValuePtr(credentials));
  }

  ignore_content_length = rb_funcall(request, rb_intern("ignore_content_length"), 0);
  if (RTEST(ignore_content_length)) {
    curl_easy_setopt(curl, CURLOPT_IGNORE_CONTENT_LENGTH, 1);
  }

  insecure = rb_funcall(request, rb_intern("insecure"), 0);
  if(RTEST(insecure)) {
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
  }

  ssl_version = rb_funcall(request, rb_intern("ssl_version"), 0);
  if(RTEST(ssl_version)) {
    VALUE ssl_version_str = rb_funcall(ssl_version, rb_intern("to_s"), 0);
    char* version = StringValuePtr(ssl_version_str);
    if(strcmp(version, "SSLv2") == 0) {
      curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_SSLv2);
    } else if(strcmp(version, "SSLv3") == 0) {
      curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_SSLv3);
    } else if(strcmp(version, "TLSv1") == 0) {
      curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
    } else {
      rb_raise(eUnsupportedSSLVersion, "Unsupported SSL version: %s", version);
    }
  }

  cacert = rb_funcall(request, rb_intern("cacert"), 0);
  if(RTEST(cacert)) {
    curl_easy_setopt(curl, CURLOPT_CAINFO, StringValuePtr(cacert));
  }

  buffer_size = rb_funcall(request, rb_intern("buffer_size"), 0);
  if (RTEST(buffer_size)) {
     curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, NUM2LONG(buffer_size));
  }

  if(state->debug_file) {
    curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
    curl_easy_setopt(curl, CURLOPT_STDERR, state->debug_file);
  }
}