iSulad/src/http/http.c

416 lines
12 KiB
C
Raw Normal View History

2019-09-30 10:53:41 -04:00
/******************************************************************************
* Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
* iSulad licensed under the Mulan PSL v1.
* You can use this software according to the terms and conditions of the Mulan PSL v1.
* You may obtain a copy of Mulan PSL v1 at:
* http://license.coscl.org.cn/MulanPSL
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
* PURPOSE.
* See the Mulan PSL v1 for more details.
* Author: tanyifeng
* Create: 2018-11-08
* Description: provide container http function
******************************************************************************/
#include <curl/curl.h>
#include <curl/easy.h>
#include <string.h>
#include "http.h"
#include "buffer.h"
#include "log.h"
#include "utils.h"
size_t fwrite_buffer(const char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
{
size_t size = eltsize * nmemb;
struct Buffer *buffer = buffer_;
int status = 0;
status = buffer_append(buffer, ptr, size);
if (status != 0) {
ERROR("Failed to write buffer\n");
}
return size;
}
size_t fwrite_file(const void *ptr, size_t size, size_t nmemb, void *stream)
{
size_t written = fwrite(ptr, size, nmemb, (FILE *)stream);
return written;
}
size_t fwrite_null(char *ptr, size_t eltsize, size_t nmemb, void *strbuf)
{
return eltsize * nmemb;
}
void free_http_get_options(struct http_get_options *options)
{
if (options == NULL) {
return;
}
free(options->accepts);
options->accepts = NULL;
free(options->authorization);
options->authorization = NULL;
free(options->unix_socket_path);
options->unix_socket_path = NULL;
/* The options->output is a FILE pointer, we should not free it here */
free(options);
return;
}
void http_global_init(void)
{
curl_global_init(CURL_GLOBAL_NOTHING);
}
void http_global_cleanup(void)
{
curl_global_cleanup();
}
struct curl_slist *http_get_chunk_header(const struct http_get_options *options)
{
int ret = 0;
int nret;
size_t len = 0;
struct curl_slist *chunk = NULL;
char *header = NULL;
if (options->with_header_auth && options->authorization) {
if (strlen(options->authorization) > (SIZE_MAX - strlen("Authorization: ")) - 1) {
ERROR("Invalid authorization option");
ret = -1;
goto out;
}
len = strlen(options->authorization) + strlen("Authorization: ") + 1;
header = util_common_calloc_s(len);
if (header == NULL) {
ERROR("Out of memory");
ret = -1;
goto out;
}
nret = snprintf(header, len, "Authorization: %s", options->authorization);
if (nret < 0 || (size_t)nret >= len) {
2019-09-30 10:53:41 -04:00
ERROR("Failed to print string");
ret = -1;
goto out;
}
chunk = curl_slist_append(chunk, header);
free(header);
header = NULL;
}
if (options->with_header_json) {
chunk = curl_slist_append(chunk, "Content-Type: application/json");
// Disable "Expect: 100-continue"
chunk = curl_slist_append(chunk, "Expect:");
}
if (options->with_header_accept && options->accepts) {
if (strlen(options->accepts) > (SIZE_MAX - strlen("Accept: ")) - 1) {
ERROR("Invalid accepts option");
ret = -1;
goto out;
}
len = strlen(options->accepts) + strlen("Accept: ") + 1;
header = util_common_calloc_s(len);
if (header == NULL) {
ERROR("Out of memory");
ret = -1;
goto out;
}
nret = snprintf(header, len, "Accept: %s", options->accepts);
if (nret < 0 || (size_t)nret >= len) {
2019-09-30 10:53:41 -04:00
ERROR("Failed to print string");
ret = -1;
goto out;
}
chunk = curl_slist_append(chunk, header);
free(header);
header = NULL;
}
out:
if (ret) {
curl_slist_free_all(chunk);
chunk = NULL;
}
free(header);
return chunk;
}
static int http_custom_options(CURL *curl_handle, const struct http_get_options *options)
{
int ret = 0;
if (curl_handle == NULL || options == NULL) {
return -1;
}
if (options->unix_socket_path) {
curl_easy_setopt(curl_handle, CURLOPT_UNIX_SOCKET_PATH, options->unix_socket_path);
}
if (options->with_head) {
curl_easy_setopt(curl_handle, CURLOPT_HEADER, 1L);
}
if (options->with_body == 0) {
curl_easy_setopt(curl_handle, CURLOPT_NOBODY, 1L);
}
/* disable progress meter, set to 0L to enable and disable debug output */
if (options->show_progress == 0) {
curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L);
} else if (options->show_progress && options->progressinfo && options->progress_info_op) {
curl_easy_setopt(curl_handle, CURLOPT_PROGRESSFUNCTION, options->progress_info_op);
/* pass the struct pointer into the progress function */
curl_easy_setopt(curl_handle, CURLOPT_PROGRESSDATA, options->progressinfo);
curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 0L);
} else {
curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 0L);
}
if (options->input) {
curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, options->input);
curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, options->input_len);
curl_easy_setopt(curl_handle, CURLOPT_POST, 1L);
}
return ret;
}
static void close_file(FILE *pagefile)
{
if (pagefile != NULL) {
fclose(pagefile);
}
}
static void free_rpath(char *rpath)
{
free(rpath);
}
static void check_buf_len(const char *errbuf)
{
size_t len = 0;
len = strlen(errbuf);
if (len > 0) {
fprintf(stderr, "%s%s", errbuf, ((errbuf[len - 1] != '\n') ? "\n" : ""));
}
}
static void buffer_empty_on_condition(struct http_get_options *options)
{
if (options == NULL) {
return;
}
if (options->output && options->outputtype == HTTP_REQUEST_STRBUF) {
buffer_empty(options->output);
}
if (options->with_header_auth && options->authorization) {
options->with_header_auth = 0;
}
}
static void curl_getinfo_on_condition(long *response_code, CURL *curl_handle, char **tmp)
{
if (response_code != NULL) {
curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, response_code);
}
curl_easy_getinfo(curl_handle, CURLINFO_REDIRECT_URL, tmp);
}
static int ensure_path_file(char **rpath, const struct http_get_options *options, FILE **pagefile)
{
if (util_ensure_path(rpath, options->output)) {
return -1;
}
*pagefile = util_fopen(*rpath, "w+");
if (*pagefile == NULL) {
ERROR("Failed to open file %s\n", options->output);
return -1;
}
return 0;
}
static struct curl_slist *set_custom_header(CURL *curl_handle, const struct http_get_options *options)
{
struct curl_slist *chunk = NULL;
chunk = http_get_chunk_header(options);
if (chunk) {
/* set our custom set of headers */
curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, chunk);
}
return chunk;
}
int http_request(const char *url, struct http_get_options *options, long *response_code, int recursive_len)
{
#define MAX_REDIRCT_NUMS 32
CURL *curl_handle = NULL;
CURLcode curl_result = CURLE_OK;
struct curl_slist *chunk = NULL;
FILE *pagefile = NULL;
char *rpath = NULL;
int ret = 0;
char errbuf[CURL_ERROR_SIZE] = { 0 };
bool strbuf_args;
bool file_args;
char *redir_url = NULL;
char *tmp = NULL;
if (recursive_len + 1 >= MAX_REDIRCT_NUMS) {
ERROR("reach the max redirect num");
return -1;
}
http_global_init();
/* init the curl session */
curl_handle = curl_easy_init();
if (curl_handle == NULL) {
return -1;
}
/* set URL to get here */
curl_easy_setopt(curl_handle, CURLOPT_URL, url);
curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1L);
/* complete connection within 5 seconds */
curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, 5L);
/* provide a buffer to store errors in */
curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, errbuf);
curl_easy_setopt(curl_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0);
ret = http_custom_options(curl_handle, options);
if (ret) {
goto out;
}
chunk = set_custom_header(curl_handle, options);
strbuf_args = options->output && options->outputtype == HTTP_REQUEST_STRBUF;
file_args = options->output && options->outputtype == HTTP_REQUEST_FILE;
if (strbuf_args) {
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, options->output);
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, fwrite_buffer);
} else if (file_args) {
/* open the file */
if (ensure_path_file(&rpath, options, &pagefile) == -1) {
goto out;
}
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, pagefile);
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, fwrite_file);
} else {
/* do nothing */
}
/* get it! */
curl_result = curl_easy_perform(curl_handle);
if (curl_result != CURLE_OK) {
check_buf_len(errbuf);
ret = -1;
} else {
curl_getinfo_on_condition(response_code, curl_handle, &tmp);
if (tmp) {
redir_url = util_strdup_s(tmp);
}
}
out:
close_file(pagefile);
free_rpath(rpath);
/* cleanup curl stuff */
curl_easy_cleanup(curl_handle);
curl_slist_free_all(chunk);
if (redir_url) {
buffer_empty_on_condition(options);
if (http_request(redir_url, options, response_code, recursive_len + 1)) {
ERROR("Failed to get http request\n");
ret = -1;
}
free(redir_url);
}
return ret;
}
int authz_http_request(const char *username, const char *action, char **resp)
{
char *request_body = NULL;
char err_msg[AUTHZ_ERROR_MSG_SIZE] = { 0 };
long response_code = 0;
int ret = 0;
int nret = 0;
2019-09-30 10:53:41 -04:00
size_t length = 0;
struct http_get_options *options = NULL;
if (strlen(username) > ((SIZE_MAX - strlen(action)) - strlen(":")) - 1) {
ERROR("Invalid arguments");
return -1;
}
length = strlen(username) + strlen(":") + strlen(action) + 1;
request_body = util_common_calloc_s(length);
if (request_body == NULL) {
ERROR("Out of memory");
*resp = util_strdup_s("Inernal server error: Out of memory");
return -1;
}
nret = snprintf(request_body, length, "%s:%s", username, action);
if (nret < 0 || (size_t)nret >= length) {
2019-09-30 10:53:41 -04:00
ERROR("Failed to print string");
free(request_body);
return -1;
}
options = util_common_calloc_s(sizeof(struct http_get_options));
if (options == NULL) {
ERROR("Failed to malloc http_get_options");
*resp = util_strdup_s("Inernal server error: Out of memory");
free(request_body);
return -1;
}
options->with_head = 1;
options->with_header_json = 1;
options->input = request_body;
options->input_len = strlen(request_body);
options->unix_socket_path = util_strdup_s(AUTHZ_UNIX_SOCK);
ret = http_request(AUTHZ_REQUEST_URL, options, &response_code, 0);
if (ret != 0) {
ERROR("Failed to request authz plugin. Is server running ?");
*resp = util_strdup_s("Failed to request authz plugin. Is server running ?");
ret = -1;
goto out;
}
if (response_code != StatusOK) {
ret = -1;
nret = snprintf(err_msg, sizeof(err_msg), "action '%s' for user '%s': permission denied", action, username);
if (nret < 0 || (size_t)nret >= sizeof(err_msg)) {
2019-09-30 10:53:41 -04:00
ERROR("Out of memory");
*resp = util_strdup_s("Inernal server error: Out of memory");
goto out;
}
*resp = util_strdup_s(err_msg);
goto out;
}
out:
free(request_body);
if (options != NULL) {
free(options->unix_socket_path);
free(options);
}
return ret;
}
2019-12-25 15:50:34 +08:00