Implemented file upload feature 46/85846/5
authorSeonah Moon <seonah1.moon@samsung.com>
Mon, 29 Aug 2016 08:42:29 +0000 (17:42 +0900)
committerSeonah Moon <seonah1.moon@samsung.com>
Mon, 29 Aug 2016 10:43:41 +0000 (19:43 +0900)
- PUT
- POST Multipart

Change-Id: I1d24125b861f52560a7aaadf225bb613f51a743c
Signed-off-by: Seonah Moon <seonah1.moon@samsung.com>
include/http.h
include/http_internal.h [new file with mode: 0644]
include/http_private.h
src/http_request.c
src/http_session.c
src/http_transaction.c
test/http_test.c

index 9721b5e..1dbada6 100644 (file)
@@ -598,7 +598,7 @@ int http_transaction_pause(http_transaction_h http_transaction, http_pause_type_
  * @retval  #HTTP_ERROR_INVALID_OPERATION  Invalid operation
  * @retval  #HTTP_ERROR_NOT_SUPPORTED  Not Supported
  */
-int http_transaction_set_ready_to_write(http_transaction_h http_transaction, bool read_to_write);
+int http_transaction_set_ready_to_write(http_transaction_h http_transaction, bool ready_to_write);
 
 /**
  * @brief Sets the interface name.
diff --git a/include/http_internal.h b/include/http_internal.h
new file mode 100644 (file)
index 0000000..8b81451
--- /dev/null
@@ -0,0 +1,42 @@
+/**
+ * @brief Enumeration for the http multipart types.
+ * @since_tizen 3.0
+ */
+typedef enum {
+           HTTP_MULTIPART_CONTENTS,
+           HTTP_MULTIPART_FILE,
+} http_formdata_type_e;
+
+/**
+ * @brief Sets file path for uploading a file.
+ * @since_tizen 3.0
+ * @remarks    It is used with #HTTP_METHOD_PUT
+ * @param[in]  http_transaction The http transaction handle
+ * @param[in]  file_path               The path for file
+ * @return 0 on success, otherwise negative error value
+ * @retval  #HTTP_ERROR_NONE  Successful
+ * @retval  #HTTP_ERROR_INVALID_PARAMETER  Invalid parameter
+ * @retval  #HTTP_ERROR_INVALID_OPERATION  Invalid operation
+ * @retval  #HTTP_ERROR_NOT_SUPPORTED  Not Supported
+ */
+int http_transaction_request_set_upload_file(http_transaction_h http_transaction, const char *file_path);
+
+/**
+ * @brief Adds the multipart/form-data.
+ * @since_tizen 3.0
+ * @remarks    It is used with #HTTP_METHOD_POST
+ * @param[in]  http_transaction        The http transaction handle
+ * @param[in]  multipart_type  The type of form-data
+ * @param[in]  part_name       The name of part
+ * @param[in]  value   The value of part \n
+ *                                             It will be file path or content value
+ * @param[in]  content_type            The content type of value (e.g text/html, image/jpeg, etc) \n
+ *                                                             It will be #NULL
+ * @return 0 on success, otherwise negative error value
+ * @retval  #HTTP_ERROR_NONE  Successful
+ * @retval  #HTTP_ERROR_INVALID_PARAMETER  Invalid parameter
+ * @retval  #HTTP_ERROR_INVALID_OPERATION  Invalid operation
+ * @retval  #HTTP_ERROR_NOT_SUPPORTED  Not Supported
+ */
+int http_transaction_request_add_multipart(http_transaction_h http_transaction,
+               http_formdata_type_e multipart_type, const char *part_name, const char *value, const char *content_type);
index 7bd4b94..dfab58f 100644 (file)
@@ -121,6 +121,11 @@ typedef struct {
        gchar *cookie;
        GQueue* body_queue;
        gint tot_size;
+       struct curl_httppost *formpost;
+       struct curl_httppost *lastptr;
+       gchar *upload_file;
+       FILE *fp;
+       curl_off_t upload_size;
        http_version_e http_version;
 } __http_request_h;
 
@@ -145,6 +150,7 @@ typedef struct {
        gchar *interface_name;
        int timeout;
        int write_event;
+       bool upload_event;
        bool verify_peer;
        gchar *ca_path;
        gchar error[CURL_ERROR_SIZE];
@@ -204,6 +210,7 @@ struct curl_slist* _get_header_list(http_transaction_h http_transaction);
 
 int _get_request_body_size(http_transaction_h http_transaction, int *body_size);
 int _read_request_body(http_transaction_h http_transaction, gchar **body);
+curl_off_t _get_upload_file_size(http_transaction_h http_transaction);
 void __parse_response_header(gchar *buffer, size_t written, gpointer user_data);
 int _generate_session_id(void);
 void _add_transaction_to_list(http_transaction_h http_transaction);
@@ -214,6 +221,10 @@ http_auth_scheme_e _get_http_auth_scheme(bool proxy_auth, curl_http_auth_scheme_
 int _set_authentication_info(http_transaction_h http_transaction);
 gchar* parse_values(const gchar* string, int from_index, int to_index);
 
+FILE* _get_upload_file(http_transaction_h http_transaction);
+void _open_upload_file(http_transaction_h http_transaction);
+void _close_upload_file(__http_transaction_h *http_transaction);
+
 #ifdef __cplusplus
 }
 #endif
index 2233425..9ebb178 100644 (file)
  * limitations under the License.
  */
 
+#include <sys/stat.h>
+
 #include "http.h"
+#include "http_internal.h"
 #include "http_private.h"
 
+static void _add_multipart_data(http_transaction_h http_transaction,
+               const char *part_name, const char *value,
+               const char *content_type, http_formdata_type_e type);
+
 API int http_transaction_request_set_method(http_transaction_h http_transaction, http_method_e method)
 {
        _retvm_if(_http_is_init() == false, HTTP_ERROR_INVALID_OPERATION,
@@ -220,6 +227,42 @@ API int http_transaction_request_write_body(http_transaction_h http_transaction,
        return HTTP_ERROR_NONE;
 }
 
+API int http_transaction_request_set_upload_file(http_transaction_h http_transaction, const char *file_path)
+{
+       _retvm_if(_http_is_init() == false, HTTP_ERROR_INVALID_OPERATION,
+                       "http isn't initialized");
+       _retvm_if(http_transaction == NULL, HTTP_ERROR_INVALID_PARAMETER,
+                       "parameter(http_transaction) is NULL\n");
+       _retvm_if(file_path == NULL, HTTP_ERROR_INVALID_PARAMETER,
+                       "parameter(file_path) is NULL\n");
+
+       __http_transaction_h *transaction = (__http_transaction_h *)http_transaction;
+       __http_request_h *request = transaction->request;
+
+       request->upload_file = strdup(file_path);
+
+       return HTTP_ERROR_NONE;
+}
+
+API int http_transaction_request_add_multipart(http_transaction_h http_transaction,
+                                                               http_formdata_type_e formdata_type,
+                                                               const char *part_name, const char *path,
+                                                               const char *content_type)
+{
+       _retvm_if(_http_is_init() == false, HTTP_ERROR_INVALID_OPERATION,
+                       "http isn't initialized");
+       _retvm_if(http_transaction == NULL, HTTP_ERROR_INVALID_PARAMETER,
+                       "parameter(http_transaction) is NULL\n");
+       _retvm_if(part_name == NULL, HTTP_ERROR_INVALID_PARAMETER,
+                       "parameter(part_name) is NULL\n");
+       _retvm_if(path == NULL, HTTP_ERROR_INVALID_PARAMETER,
+                       "parameter(path) is NULL\n");
+
+       _add_multipart_data(http_transaction, part_name, path, content_type, formdata_type);
+
+       return HTTP_ERROR_NONE;
+}
+
 int _get_request_body_size(http_transaction_h http_transaction, int *body_size)
 {
        _retvm_if(http_transaction == NULL, HTTP_ERROR_INVALID_PARAMETER,
@@ -235,6 +278,46 @@ int _get_request_body_size(http_transaction_h http_transaction, int *body_size)
        return HTTP_ERROR_NONE;
 }
 
+void _open_upload_file(http_transaction_h http_transaction)
+{
+       __http_transaction_h *transaction = (__http_transaction_h *)http_transaction;
+       __http_request_h *request = transaction->request;
+       struct stat file_info;
+
+       stat(request->upload_file, &file_info);
+       request->fp = fopen(request->upload_file, "rb");
+       request->upload_size = (curl_off_t)file_info.st_size;
+}
+
+void _close_upload_file(__http_transaction_h *transaction)
+{
+       __http_request_h *request = transaction->request;
+
+       if (request->fp) {
+               fclose(request->fp);
+               request->fp = NULL;
+       }
+}
+
+FILE* _get_upload_file(http_transaction_h http_transaction)
+{
+       __http_transaction_h *transaction = (__http_transaction_h *)http_transaction;
+       __http_request_h *request = transaction->request;
+
+       return request->fp;
+}
+
+curl_off_t _get_upload_file_size(http_transaction_h http_transaction)
+{
+       _retvm_if(http_transaction == NULL, HTTP_ERROR_INVALID_PARAMETER,
+                       "parameter(http_transaction) is NULL\n");
+
+       __http_transaction_h *transaction = (__http_transaction_h *)http_transaction;
+       __http_request_h *request = transaction->request;
+
+       return request->upload_size;
+}
+
 //LCOV_EXCL_START
 int _read_request_body(http_transaction_h http_transaction, char **body)
 {
@@ -268,6 +351,11 @@ int _read_request_body(http_transaction_h http_transaction, char **body)
        for (index = 0; index < len; index++) {
 
                ptr = (gchar*)g_queue_pop_head(request->body_queue);
+               if (!ptr) {
+                       DBG("body_queue is empty\n");
+                       break;
+               }
+
                body_size = strlen(ptr);
 
                new_len = curr_len + body_size;
@@ -285,4 +373,33 @@ int _read_request_body(http_transaction_h http_transaction, char **body)
 
        return HTTP_ERROR_NONE;
 }
+
+static void _add_multipart_data(http_transaction_h http_transaction,
+               const char *part_name, const char *value,
+               const char *content_type, http_formdata_type_e type)
+{
+       __http_transaction_h *transaction = (__http_transaction_h *)http_transaction;
+       __http_request_h *request = transaction->request;
+       CURLformoption option = CURLFORM_COPYCONTENTS;
+
+       if (type == HTTP_MULTIPART_CONTENTS)
+               option = CURLFORM_COPYCONTENTS;
+       else if (type == HTTP_MULTIPART_FILE)
+               option = CURLFORM_FILE;
+
+       if (content_type) {
+               curl_formadd(&(request->formpost),
+                               &(request->lastptr),
+                               CURLFORM_COPYNAME, part_name,
+                               option, value,
+                               CURLFORM_CONTENTTYPE, content_type,
+                               CURLFORM_END);
+       } else {
+               curl_formadd(&(request->formpost),
+                               &(request->lastptr),
+                               CURLFORM_COPYNAME, part_name,
+                               option, value,
+                               CURLFORM_END);
+       }
+}
 //LCOV_EXCL_STOP
index 0b37433..af38228 100644 (file)
@@ -39,11 +39,13 @@ void _check_curl_multi_status(gpointer user_data)
                        curl_easy_getinfo(curl_easy, CURLINFO_PRIVATE, &transaction);
                        curl_easy_getinfo(curl_easy, CURLINFO_EFFECTIVE_URL, &url);
 
+                       if (transaction->upload_event)
+                               _close_upload_file(transaction);
+
                        DBG("Completed -%s: result(%d)\n", url, curl_code);
 
                        switch (curl_code) {
                        case CURLE_OK:
-
                                if (transaction->completed_cb) {
                                        long http_auth = _CURL_HTTP_AUTH_NONE;
                                        long proxy_auth = _CURL_HTTP_AUTH_NONE;
@@ -89,6 +91,8 @@ void _check_curl_multi_status(gpointer user_data)
                                        transaction->aborted_cb(transaction, HTTP_ERROR_OPERATION_TIMEDOUT, transaction->aborted_user_data);
                                break;
                        default:
+                               if (transaction->aborted_cb)
+                                       transaction->aborted_cb(transaction, HTTP_ERROR_OPERATION_FAILED, transaction->aborted_user_data);
                                break;
                        }
 
index 6c462d3..33302ec 100644 (file)
@@ -109,6 +109,23 @@ size_t __handle_write_cb(gchar *ptr, size_t size, size_t nmemb, gpointer user_da
 
        return body_size;
 }
+
+size_t __handle_upload_cb(gchar *ptr, size_t size, size_t nmemb, gpointer user_data)
+{
+       __http_transaction_h *transaction = (__http_transaction_h *)user_data;
+       size_t recommended_size;
+       curl_off_t nread;
+       FILE *fp = _get_upload_file(transaction);
+
+       recommended_size = fread(ptr, size, nmemb, fp);
+       nread = (curl_off_t)recommended_size;
+       DBG("Read %" CURL_FORMAT_CURL_OFF_T " bytes from file", nread);
+
+       if (transaction->write_cb)
+               transaction->write_cb(transaction, recommended_size, transaction->write_user_data);
+
+       return recommended_size;
+}
 //LCOV_EXCL_STOP
 
 size_t __http_debug_received(CURL *easy_handle, curl_infotype type, gchar *byte, size_t size, void *user_data)
@@ -298,7 +315,6 @@ int _transaction_submit(gpointer user_data)
                        curl_auth_scheme = _get_http_curl_auth_scheme(auth_scheme);
 
                        if (transaction->proxy_auth_type) {
-
                                curl_easy_setopt(transaction->easy_handle, CURLOPT_PROXYAUTH, curl_auth_scheme);
                                curl_easy_setopt(transaction->easy_handle, CURLOPT_PROXYUSERPWD, credentials);
 
@@ -333,6 +349,8 @@ int _transaction_submit(gpointer user_data)
 
        _get_request_body_size(transaction, &body_size);
 
+
+       /* Setup for POST method */
        if (transaction->write_event) {
                if (content_len > 0 && content_len <= body_size)
                        write_event = FALSE;
@@ -341,7 +359,7 @@ int _transaction_submit(gpointer user_data)
                DBG("The write_event is %d.\n", write_event);
        }
 
-       if ((_get_method(request->method) == HTTP_METHOD_POST) && !write_event) {
+       if ((_get_method(request->method) == HTTP_METHOD_POST) && !write_event && !request->formpost) {
                gchar *body = NULL;
 
                _read_request_body(transaction, &body);
@@ -359,6 +377,19 @@ int _transaction_submit(gpointer user_data)
                curl_easy_setopt(transaction->easy_handle, CURLOPT_READDATA, transaction);
        }
 
+       /* Mulipart POST */
+       if (request->formpost)
+               curl_easy_setopt(transaction->easy_handle, CURLOPT_HTTPPOST, request->formpost);
+
+       /* Setup for PUT method */
+       if (transaction->upload_event) {
+               _open_upload_file(transaction);
+               curl_easy_setopt(transaction->easy_handle, CURLOPT_UPLOAD, 1L);
+               curl_easy_setopt(transaction->easy_handle, CURLOPT_READFUNCTION, __handle_upload_cb);
+               curl_easy_setopt(transaction->easy_handle, CURLOPT_READDATA, transaction);
+               curl_easy_setopt(transaction->easy_handle, CURLOPT_INFILESIZE_LARGE, _get_upload_file_size(transaction));
+       }
+
        curl_easy_setopt(transaction->easy_handle, CURLOPT_NOPROGRESS, FALSE);
        curl_easy_setopt(transaction->easy_handle, CURLOPT_PROGRESSFUNCTION, __progress_cb);
        curl_easy_setopt(transaction->easy_handle, CURLOPT_PROGRESSDATA, transaction);
@@ -460,11 +491,21 @@ API int http_session_open_transaction(http_session_h http_session, http_method_e
 
        transaction->request->host_uri = NULL;
        transaction->request->method = _get_http_method(method);
+       if (method == HTTP_METHOD_PUT)
+               transaction->upload_event = TRUE;
+       else
+               transaction->upload_event = FALSE;
+
        transaction->request->encoding = NULL;
        transaction->request->cookie = NULL;
        transaction->request->http_version = HTTP_VERSION_1_1;
        transaction->request->body_queue = g_queue_new();
        transaction->request->tot_size = 0;
+       transaction->request->upload_size = 0;
+       transaction->request->upload_file = NULL;
+       transaction->request->fp = NULL;
+       transaction->request->formpost = NULL;
+       transaction->request->lastptr = NULL;
 
        transaction->response->status_text = NULL;
 
@@ -592,6 +633,16 @@ API int http_transaction_destroy(http_transaction_h http_transaction)
                        if (request->body_queue != NULL)
                                g_queue_free(request->body_queue);
 
+                       if (request->formpost) {
+                               curl_formfree(request->formpost);
+                               request->formpost = NULL;
+                       }
+
+                       if (request->upload_file != NULL) {
+                               free(request->upload_file);
+                               request->upload_file = NULL;
+                       }
+
                        free(request);
                }
 
index 1f71de7..6f34cb6 100644 (file)
@@ -21,6 +21,7 @@
 #include <gio/gio.h>
 
 #include "http.h"
+#include "http_internal.h"
 
 #define ERR(x, y) printf("[ERR] %s(%d)\n", x, y)
 #define PRG(x, y) printf("[PRG] %s(%p)\n", x, y)
@@ -42,7 +43,8 @@ void __transaction_body_cb(http_transaction_h transaction, char *body, size_t si
 {
        PRG("transaction_body_cb", transaction);
        int written = size * nmemb;
-       DBG("Received: %d\n", written);
+       DBG("Received length: %d\n", written);
+       DBG("Received body: %s\n", body);
 }
 
 void __transaction_write_cb(http_transaction_h transaction, int recommended_chunk_size, void *user_data)
@@ -260,6 +262,75 @@ int test_simple_post(void)
        return 1;
 }
 
+int test_multipart_post(void)
+{
+       int ret;
+       http_transaction_h transaction;
+       char *file_path = "/home/test.txt";
+       char *file_name = "test.txt";
+
+       ret = http_session_open_transaction(session, HTTP_METHOD_POST, &transaction);
+       if (ret != 0) {
+               ERR("Fail to open transaction", ret);
+               return 0;
+       }
+       ret = http_transaction_request_set_uri(transaction, "http://posttestserver.com/post.php");
+       if (ret != 0) {
+               ERR("Fail to set URI", ret);
+               return 0;
+       }
+
+       ret = http_transaction_request_add_multipart(transaction, HTTP_MULTIPART_FILE,
+                       "sendfile", file_path, NULL);
+
+       ret = http_transaction_request_add_multipart(transaction, HTTP_MULTIPART_CONTENTS,
+                       "filename", file_name, NULL);
+
+       http_transaction_set_received_header_cb(transaction, __transaction_header_cb, NULL);
+       http_transaction_set_received_body_cb(transaction, __transaction_body_cb, NULL);
+       http_transaction_set_completed_cb(transaction, __transaction_completed_cb, NULL);
+       http_transaction_set_aborted_cb(transaction, __transaction_aborted_cb, NULL);
+       http_transaction_set_progress_cb(transaction, __transaction_progress_cb, NULL);
+
+       http_transaction_submit(transaction);
+
+       return 1;
+}
+
+int test_put(void)
+{
+       http_transaction_h transaction;
+       const char *file_path = "/home/test.txt";
+       int ret = 0;
+
+       ret = http_session_open_transaction(session, HTTP_METHOD_PUT, &transaction);
+       if (ret != 0) {
+               ERR("Fail to open transaction", ret);
+               return 0;
+       }
+
+       ret = http_transaction_request_set_uri(transaction, "http://posttestserver.com/post.php");
+       if (ret != 0) {
+               ERR("Fail to set URI", ret);
+               return 0;
+       }
+
+       ret = http_transaction_request_set_upload_file(transaction, file_path);
+       if (ret != 0) {
+               ERR("Fail to set file path", ret);
+               return 0;
+       }
+
+       _register_callbacks(transaction);
+       ret = http_transaction_submit(transaction);
+       if (ret != 0) {
+               ERR("Fail to submit transaction", ret);
+               return 0;
+       }
+
+       return 1;
+}
+
 int test_simple_authentication_get(void)
 {
        int ret;
@@ -285,7 +356,6 @@ int test_simple_authentication_get(void)
 
        _register_callbacks(transaction);
        ret = http_transaction_submit(transaction);
-
        if (ret != 0) {
                ERR("Fail to submit transaction", ret);
                return 0;
@@ -316,8 +386,9 @@ gboolean test_thread(GIOChannel *source, GIOCondition condition, gpointer data)
                printf("5       - Simple GET\n");
                printf("6       - Multiple GET\n");
                printf("7       - Simple POST\n");
-               printf("8       - Simple Authentication GET\n");
-               printf("9       - \n");
+               printf("8       - Multipart POST\n");
+               printf("9       - Simple Authentication GET\n");
+               printf("a       - Upload file (PUT)\n");
                printf("0       - Exit \n");
                printf("ENTER  - Show options menu.......\n");
        }
@@ -345,8 +416,14 @@ gboolean test_thread(GIOChannel *source, GIOCondition condition, gpointer data)
                rv = test_simple_post();
                break;
        case '8':
+               rv = test_multipart_post();
+               break;
+       case '9':
                rv = test_simple_authentication_get();
                break;
+       case 'a':
+               rv = test_put();
+               break;
        }
 
        if (rv == 1)