From 0f7f9842ff36e5d0bba5345bcdba2ab9fd6eed76 Mon Sep 17 00:00:00 2001 From: Rajkumar Date: Sat, 1 Jul 2023 11:31:28 +0530 Subject: [PATCH 1/7] Replace form_type and header_type with pair_type derived type --- example/post.f90 | 7 ++- example/post_form_data.f90 | 14 +++-- example/response_header.f90 | 18 +++--- src/http.f90 | 3 +- src/http/http_client.f90 | 52 +++++++++--------- src/http/http_form.f90 | 15 ----- src/http/http_header.f90 | 90 ------------------------------ src/http/http_pair.f90 | 106 ++++++++++++++++++++++++++++++++++++ src/http/http_request.f90 | 8 +-- src/http/http_response.f90 | 16 +++--- test/test_get.f90 | 12 ++-- test/test_header.f90 | 20 +++---- test/test_post.f90 | 6 +- 13 files changed, 186 insertions(+), 181 deletions(-) delete mode 100644 src/http/http_form.f90 delete mode 100644 src/http/http_header.f90 create mode 100644 src/http/http_pair.f90 diff --git a/example/post.f90 b/example/post.f90 index 9fe2402..7ecd44d 100644 --- a/example/post.f90 +++ b/example/post.f90 @@ -1,13 +1,14 @@ program post_request ! This program demonstrates sending JSON data using POST request and printing the ! status, length of the body, method, and the body of the response. - use http, only: response_type, request, HTTP_POST, header_type + use http, only: response_type, request, HTTP_POST, pair_type implicit none type(response_type) :: response character(:), allocatable :: json_data - type(header_type), allocatable :: req_header(:) + type(pair_type), allocatable :: req_header(:) - req_header = [header_type('Content-Type', 'application/json')] + ! Storing request header in array of pair_type object + req_header = [pair_type('Content-Type', 'application/json')] ! JSON data we want to send json_data = '{"name":"Jhon","role":"developer"}' diff --git a/example/post_form_data.f90 b/example/post_form_data.f90 index 10c493b..dc9b4b2 100644 --- a/example/post_form_data.f90 +++ b/example/post_form_data.f90 @@ -1,13 +1,15 @@ program post_form_data - ! This program demonstrates sending Form data using POST request and printing the - ! status, length of the body, method, and the body of the response. - use http, only: response_type, request, HTTP_POST, header_type, form_type + ! This program demonstrates sending Form data using POST request and printing + ! the status, length of the body, method, and the body of the response. + use http, only: response_type, request, HTTP_POST, pair_type implicit none type(response_type) :: response - type(header_type), allocatable :: req_header(:) - type(form_type), allocatable :: form_data(:) + type(pair_type), allocatable :: req_header(:) + type(pair_type), allocatable :: form_data(:) - form_data = [form_type('param1', 'value1'), form_type('param2', 'value2')] + ! Storing form data in a array of pair_type object, each pair_type object + ! represent a single form field + form_data = [pair_type('param1', 'value1'), pair_type('param2', 'value2')] response = request(url='https://httpbin.org/post', method=HTTP_POST, form=form_data) diff --git a/example/response_header.f90 b/example/response_header.f90 index a5360a7..496f452 100644 --- a/example/response_header.f90 +++ b/example/response_header.f90 @@ -2,20 +2,22 @@ program response_header ! This program demonstrates sending user-provided headers in a GET request ! and iterating over the headers of the response sent back by the server. use stdlib_string_type, only: string_type, write(formatted) - use http, only: response_type, request, header_type + use http, only: response_type, request, pair_type implicit none type(response_type) :: response - type(header_type), allocatable :: header(:), req_header(:) + type(pair_type), allocatable :: header(:), req_header(:) character(:), allocatable :: val integer :: i = 0 + ! Storing request header in array of pair_type object, where each pair_type + ! object represents a single header. (in header-name,header-value format) req_header = [ & - header_type('Another-One', 'Hello'), & - header_type('Set-Cookie', 'Theme-Light'), & - header_type('Set-Cookie', 'Auth-Token: 12345'), & - header_type('User-Agent', 'my user agent') & - ] + pair_type('Another-One', 'Hello'), & + pair_type('Set-Cookie', 'Theme-Light'), & + pair_type('Set-Cookie', 'Auth-Token: 12345'), & + pair_type('User-Agent', 'my user agent') & + ] response = request(url='https://httpbin.org/get', header=req_header) @@ -28,7 +30,7 @@ program response_header header = response%header ! Iterate over response headers. do i = 1, size(header) - print *, header(i)%key, ': ', header(i)%value + print *, header(i)%name, ': ', header(i)%value end do ! getting header value by header name diff --git a/src/http.f90 b/src/http.f90 index 066a0d9..a2792a9 100644 --- a/src/http.f90 +++ b/src/http.f90 @@ -3,6 +3,5 @@ module http HTTP_DELETE, HTTP_GET, HTTP_HEAD, HTTP_PATCH, HTTP_POST, HTTP_POST use http_response, only: response_type use http_client, only: request - use http_header, only : header_type - use http_form, only : form_type + use http_pair, only : pair_type end module http diff --git a/src/http/http_client.f90 b/src/http/http_client.f90 index 6d6488e..2f83963 100644 --- a/src/http/http_client.f90 +++ b/src/http/http_client.f90 @@ -21,8 +21,8 @@ module http_client use stdlib_optval, only: optval use http_request, only: request_type use http_response, only: response_type - use http_header, only: append_header, header_has_key, header_type - use http_form, only: form_type + use http_pair, only: append_pair, pair_has_name, pair_type + ! use http_form, only: pair_type implicit none @@ -64,10 +64,10 @@ function new_request(url, method, header, data, form) result(response) !! An character(len=*) argument that specifies the URL of the server. character(len=*), intent(in), optional :: data !! An optional character(len=*) argument that specifies the data to send in the request body. - type(header_type), intent(in), optional :: header(:) - !! An optional array of header_type objects that specifies the request headers to send to the server. - type(form_type), intent(in), optional :: form(:) - !! An optional array of form_type objects that specifies the form data to send in the request body. + type(pair_type), intent(in), optional :: header(:) + !! An optional array of pair_type objects that specifies the request headers to send to the server. + type(pair_type), intent(in), optional :: form(:) + !! An optional array of pair_type objects that specifies the form data to send in the request body. type(response_type) :: response !! A response_type object containing the server's response. type(request_type) :: request @@ -84,11 +84,11 @@ function new_request(url, method, header, data, form) result(response) if (present(header)) then request%header = header ! Set default request headers. - if (.not. header_has_key(header, 'user-agent')) then - call append_header(request%header, 'user-agent', 'fortran-http/0.1.0') + if (.not. pair_has_name(header, 'user-agent')) then + call append_pair(request%header, 'user-agent', 'fortran-http/0.1.0') end if else - request%header = [header_type('user-agent', 'fortran-http/0.1.0')] + request%header = [pair_type('user-agent', 'fortran-http/0.1.0')] end if ! setting the request data to be send @@ -128,7 +128,7 @@ function client_get_response(this) result(response) !! An inout argument of the client_type class that specifies the HTTP request to send. type(response_type), target :: response !! A response_type object containing the server's response. - type(c_ptr) :: curl_ptr, header_list_ptr + type(c_ptr) :: curl_ptr, header_list_ptr integer :: rc, i curl_ptr = c_null_ptr @@ -147,7 +147,7 @@ & function failed. This can occur due to insufficient memory available in the sy end if ! setting request URL - rc = curl_easy_setopt(curl_ptr, CURLOPT_URL, this%request%url ) + rc = curl_easy_setopt(curl_ptr, CURLOPT_URL, this%request%url) ! setting request method rc = set_method(curl_ptr, this%request%method, response) @@ -226,14 +226,14 @@ subroutine prepare_form_encoded_str(curl_ptr, request) end if end do ! setting the Content-Type header to application/x-www-form-urlencoded, used for sending form data - if (.not. header_has_key(request%header, 'Content-Type')) then - call append_header(request%header, 'Content-Type', 'application/x-www-form-urlencoded') + if (.not. pair_has_name(request%header, 'Content-Type')) then + call append_pair(request%header, 'Content-Type', 'application/x-www-form-urlencoded') end if end if end subroutine prepare_form_encoded_str ! This subroutine prepares a linked list of headers for an HTTP request using the libcurl library. - ! The function takes an array of header_type objects that contain the key-value pairs of the headers + ! The function takes an array of pair_type objects that contain the key-value pairs of the headers ! to include in the request. The subroutine iterates over the array and constructs a string for each ! header in the format "key:value". The subroutine then appends each string to the linked list using ! the curl_slist_append function. The resulting linked list is returned via the header_list_ptr argument. @@ -241,15 +241,15 @@ subroutine prepare_request_header_ptr(header_list_ptr, req_headers) !! This subroutine prepares a linked list of headers for an HTTP request using the libcurl library. type(c_ptr), intent(out) :: header_list_ptr !! An out argument of type c_ptr that is allocated and set to point to a linked list of headers. - type(header_type), allocatable, intent(in) :: req_headers(:) - !! An in argument of type header_type array that specifies the headers to include in the request. - character(:), allocatable :: h_key, h_val, final_header_string + type(pair_type), allocatable, intent(in) :: req_headers(:) + !! An in argument of type pair_type array that specifies the headers to include in the request. + character(:), allocatable :: h_name, h_val, final_header_string integer :: i do i = 1, size(req_headers) - h_key = req_headers(i)%key + h_name = req_headers(i)%name h_val = req_headers(i)%value - final_header_string = h_key // ':' // h_val + final_header_string = h_name // ':' // h_val header_list_ptr = curl_slist_append(header_list_ptr, final_header_string) end do end subroutine prepare_request_header_ptr @@ -368,7 +368,7 @@ end function client_response_callback function client_header_callback(ptr, size, nmemb, client_data) bind(c) !! This function is a callback function used by the libcurl library to handle HTTP headers. !! It is called for each header received from the server and stores the header in an array of - !! header_type objects in a response_type object. + !! pair_type objects in a response_type object. type(c_ptr), intent(in), value :: ptr !! An in argument of type c_ptr that points to the received header buffer. integer(kind=c_size_t), intent(in), value :: size @@ -380,7 +380,7 @@ function client_header_callback(ptr, size, nmemb, client_data) bind(c) integer(kind=c_size_t) :: client_header_callback !! An integer(kind=c_size_t) value representing the number of bytes received. type(response_type), pointer :: response - character(len=:), allocatable :: buf, h_key, h_value + character(len=:), allocatable :: buf, h_name, h_value integer :: i client_header_callback = int(0, kind=c_size_t) @@ -396,15 +396,15 @@ function client_header_callback(ptr, size, nmemb, client_data) bind(c) call c_f_str_ptr(ptr, buf, nmemb) if (.not. allocated(buf)) return - ! Parsing Header, and storing in array of header_type object + ! Parsing Header, and storing in array of pair_type object i = index(buf, ':') if(i /= 0 .and. len(buf) > 2) then - h_key = trim(buf(:i-1)) + h_name = trim(buf(:i-1)) h_value = buf(i+2 : ) h_value = h_value( : len(h_value)-2) - if(len(h_value) > 0 .and. len(h_key) > 0) then - call append_header(response%header, h_key, h_value) - ! response%header = [response%header, header_type(h_key, h_value)] + if(len(h_value) > 0 .and. len(h_name) > 0) then + call append_pair(response%header, h_name, h_value) + ! response%header = [response%header, pair_type(h_name, h_value)] end if end if deallocate(buf) diff --git a/src/http/http_form.f90 b/src/http/http_form.f90 deleted file mode 100644 index 3e98455..0000000 --- a/src/http/http_form.f90 +++ /dev/null @@ -1,15 +0,0 @@ -module http_form - !! This module contains the definition of a form_type derived type, which represents a - !!single field of an HTTP form. - implicit none - private - public :: form_type - - type :: form_type - !! A derived type representing a single field of an HTTP form. - character(:), allocatable :: name - !! The name of the form field - character(:), allocatable :: value - !! The value of the form filed - end type form_type -end module http_form \ No newline at end of file diff --git a/src/http/http_header.f90 b/src/http/http_header.f90 deleted file mode 100644 index 917402c..0000000 --- a/src/http/http_header.f90 +++ /dev/null @@ -1,90 +0,0 @@ -module http_header - - !! This module provides a simple key value type to use for HTTP headers. - !! It also provides procedures to inquire about the presence of a key and - !! its value in a header array, as well as a procedure to append new - !! headers to an existing array of headers. - - use stdlib_ascii, only: to_lower - - implicit none - private - - public :: header_type - public :: append_header - public :: get_header_value - public :: header_has_key - - type :: header_type - character(:), allocatable :: key, value - end type header_type - -contains - - subroutine append_header(header, key, value) - !! Append a new header_type instance with key and value members to the - !! header array. - type(header_type), allocatable, intent(inout) :: header(:) - !! Header array to append to - character(*), intent(in) :: key - !! Key member of header_type to append - character(*), intent(in) :: value - !! Value member of header_type to append - type(header_type), allocatable :: temp(:) - integer :: n - - if (allocated(header)) then - n = size(header) - allocate(temp(n+1)) - temp(1:n) = header - temp(n+1) = header_type(key, value) - call move_alloc(temp, header) - else - allocate(header(1)) - header(1) = header_type(key, value) - end if - end subroutine append_header - - pure function get_header_value(header, key) result(val) - !! Return the value of a requested key in a header array. If the key is - !! not found, the function returns an empty string (unallocated). If - !! there are duplicates of the key in the header array, return the value - !! of the first occurence of the key. - type(header_type), intent(in) :: header(:) - !! Header to search for key - character(*), intent(in) :: key - !! Key to search in header - character(:), allocatable :: val - !! Value of the key to return - integer :: n - - do n = 1, size(header) - if (to_lower(key) == to_lower(header(n)%key)) then - val = header(n)%value - return - end if - end do - - end function get_header_value - - pure logical function header_has_key(header, key) - !! Return .true. if key is present in header, .false. otherwise. - !! HTTP headers are case insensitive, so values are converted to - !! lowercase before comparison. - type(header_type), intent(in) :: header(:) - !! Header to search for key - character(*), intent(in) :: key - !! Key to search in header - integer :: n - - header_has_key = .false. - do n = 1, size(header) - if (to_lower(key) == to_lower(header(n)%key)) then - header_has_key = .true. - return - end if - end do - - end function header_has_key - -end module http_header \ No newline at end of file diff --git a/src/http/http_pair.f90 b/src/http/http_pair.f90 new file mode 100644 index 0000000..0588d67 --- /dev/null +++ b/src/http/http_pair.f90 @@ -0,0 +1,106 @@ +module http_pair + + !! This module provides a simple name-value type to use for HTTP Header, Form, File. + !! It also provides procedures to inquire about the presence of a name and + !! its value in a pair array, as well as a procedure to append new + !! pairs to an existing array of pairs. + + use stdlib_ascii, only: to_lower + + implicit none + private + + public :: pair_type + public :: append_pair + public :: get_pair_value + public :: pair_has_name + + type :: pair_type + !! A derived type use to store a name-value pair, it is + !! use in many instances like : + !! 1. To store request and response headers. + !! * `name` represent the header name. + !! * `value` represent the header value. + !! 2. While sending Form data, it represent a single a single field + !! of an HTTP form. + !! * `name` represents the name of the single form field. + !! * `value` represents the value of that single form field. + !! 3. While sending File, it is used to store information about + !! files to be sent. + !! * `name` represent the name of the file. + !! * `value` represent the path of the file on the local system. + character(:), allocatable :: name + !! represent the name of the pair. + character(:), allocatable :: value + !! represent the value of the pair. + end type pair_type + +contains + + subroutine append_pair(pair, name, value) + !! Append a new pair_type instance with name and value members to + !! the pair array. + type(pair_type), allocatable, intent(inout) :: pair(:) + !! pair array to append to + character(*), intent(in) :: name + !! name member of pair_type to append + character(*), intent(in) :: value + !! Value member of pair_type to append + type(pair_type), allocatable :: temp(:) + integer :: n + + if (allocated(pair)) then + n = size(pair) + allocate(temp(n+1)) + temp(1:n) = pair + temp(n+1) = pair_type(name, value) + call move_alloc(temp, pair) + else + allocate(pair(1)) + pair(1) = pair_type(name, value) + end if + end subroutine append_pair + + pure function get_pair_value(pair, name) result(val) + !! Return the value of a requested name in a pair array. If the name is + !! not found, the function returns an empty string (unallocated). If + !! there are duplicates of the name in the pair array, return the value + !! of the first occurence of the name. + type(pair_type), intent(in) :: pair(:) + !! pair to search for name + character(*), intent(in) :: name + !! name to search in pair + character(:), allocatable :: val + !! Value of the name to return + integer :: n + + do n = 1, size(pair) + if (to_lower(name) == to_lower(pair(n)%name)) then + val = pair(n)%value + return + end if + end do + + end function get_pair_value + + pure logical function pair_has_name(pair, name) + !! Return .true. if name is present in pair, .false. otherwise. + !! HTTP pairs are case insensitive, so values are converted to + !! lowercase before comparison. + type(pair_type), intent(in) :: pair(:) + !! pair to search for name + character(*), intent(in) :: name + !! name to search in pair + integer :: n + + pair_has_name = .false. + do n = 1, size(pair) + if (to_lower(name) == to_lower(pair(n)%name)) then + pair_has_name = .true. + return + end if + end do + + end function pair_has_name + +end module http_pair \ No newline at end of file diff --git a/src/http/http_request.f90 b/src/http/http_request.f90 index c32abe4..bf1c198 100644 --- a/src/http/http_request.f90 +++ b/src/http/http_request.f90 @@ -3,8 +3,8 @@ module http_request !! This module contains the definition of a request_type derived type, which !! represents an HTTP request. - use http_form , only: form_type - use http_header, only: header_type + ! use http_form , only: pair_type + use http_pair, only: pair_type use stdlib_string_type, only: string_type, to_lower, operator(==), char implicit none @@ -32,9 +32,9 @@ module http_request !! The URL-encoded form data. integer :: method !! The HTTP method of the request. - type(header_type), allocatable :: header(:) + type(pair_type), allocatable :: header(:) !! An Array of request headers. - type(form_type), allocatable :: form(:) + type(pair_type), allocatable :: form(:) !! An array of fields in an HTTP form. end type request_type end module http_request diff --git a/src/http/http_response.f90 b/src/http/http_response.f90 index ded6d9a..d715e7c 100644 --- a/src/http/http_response.f90 +++ b/src/http/http_response.f90 @@ -4,7 +4,7 @@ module http_response !! represents an HTTP response. use, intrinsic :: iso_fortran_env, only: int64 - use http_header, only: header_type, get_header_value + use http_pair, only: pair_type, get_pair_value use stdlib_string_type, only: string_type, to_lower, operator(==), char implicit none @@ -29,25 +29,25 @@ module http_response !! length of the response content. logical :: ok = .true. !! true if the response was successful else false. - type(header_type), allocatable :: header(:) + type(pair_type), allocatable :: header(:) !! An Array of response headers. contains procedure :: header_value end type response_type contains - pure function header_value(this, key) result(val) - !! The header_value function takes a key string as input and returns + pure function header_value(this, name) result(val) + !! The header_value function takes a header name string as input and returns !! the corresponding value as a string from a response_type object's !! header array. class(response_type), intent(in) :: this !! An object representing the HTTP response. - character(*), intent(in) :: key - !! The key of the header value to be retrieved. + character(*), intent(in) :: name + !! The name of the header value to be retrieved. character(:), allocatable :: val - !! The value of the specified key in the HTTP response header. + !! The value of the specified name in the HTTP response header. - val = get_header_value(this%header, key) + val = get_pair_value(this%header, name) end function header_value end module http_response diff --git a/test/test_get.f90 b/test/test_get.f90 index c58e1af..c10b5bc 100644 --- a/test/test_get.f90 +++ b/test/test_get.f90 @@ -1,12 +1,12 @@ program test_get use iso_fortran_env, only: stderr => error_unit - use http, only : response_type, request, header_type + use http, only : response_type, request, pair_type implicit none type(response_type) :: res character(:), allocatable :: msg, original_content logical :: ok = .true. - type(header_type), allocatable :: request_header(:) + type(pair_type), allocatable :: request_header(:) integer :: i original_content = '{"id":1,"title":"iPhone 9","description":"An apple mobile which is nothing like & @@ -18,10 +18,10 @@ program test_get ! setting request header request_header = [ & - header_type('Another-One', 'Hello'), & - header_type('Set-Cookie', 'Theme-Light'), & - header_type('Set-Cookie', 'Auth-Token: 12345'), & - header_type('User-Agent', 'my user agent') & + pair_type('Another-One', 'Hello'), & + pair_type('Set-Cookie', 'Theme-Light'), & + pair_type('Set-Cookie', 'Auth-Token: 12345'), & + pair_type('User-Agent', 'my user agent') & ] ! res = request(url='https://reqres.in/api/users/1', header=request_header) diff --git a/test/test_header.f90 b/test/test_header.f90 index d17b62e..7b09b22 100644 --- a/test/test_header.f90 +++ b/test/test_header.f90 @@ -1,16 +1,16 @@ program test_header use iso_fortran_env, only: stderr => error_unit - use http_header, only: get_header_value, header_has_key, header_type + use http_pair, only: get_pair_value, pair_has_name, pair_type implicit none - type(header_type), allocatable :: header(:) + type(pair_type), allocatable :: header(:) logical :: ok = .true. integer :: n header = [ & - header_type('One', '1'), & - header_type('Two', '2') & + pair_type('One', '1'), & + pair_type('Two', '2') & ] if (.not. size(header) == 2) then @@ -28,7 +28,7 @@ program test_header write(stderr, '(a)') 'Failed: Second header value is incorrect.' end if - header = [header, header_type('Three', '3')] + header = [header, pair_type('Three', '3')] if (.not. size(header) == 3) then ok = .false. @@ -41,22 +41,22 @@ program test_header end if do n = 1, size(header) - if (.not. get_header_value(header, header(n)%key) == header(n)%value) then + if (.not. get_pair_value(header, header(n)%name) == header(n)%value) then ok = .false. write(stderr, '(a)') 'Failed: Appended header value is incorrect.' end if end do do n = 1, size(header) - if (.not. header_has_key(header, header(n)%key)) then + if (.not. pair_has_name(header, header(n)%name)) then ok = .false. - write(stderr, '(a)') 'Failed: Incorrect output from header_has_key.' + write(stderr, '(a)') 'Failed: Incorrect output from pair_has_name.' end if end do - if (header_has_key(header, "Non-Existent")) then + if (pair_has_name(header, "Non-Existent")) then ok = .false. - write(stderr, '(a)') 'Failed: Incorrect output from header_has_key for non-existent key.' + write(stderr, '(a)') 'Failed: Incorrect output from pair_has_name for non-existent key.' end if if (.not. ok) then diff --git a/test/test_post.f90 b/test/test_post.f90 index 5010dd2..58483c6 100644 --- a/test/test_post.f90 +++ b/test/test_post.f90 @@ -1,14 +1,14 @@ program test_post use iso_fortran_env, only: stderr => error_unit - use http, only: request, header_type, HTTP_POST, response_type + use http, only: request, pair_type, HTTP_POST, response_type implicit none type(response_type) :: res character(:), allocatable :: json_data, original_content, msg - type(header_type), allocatable :: req_header(:) + type(pair_type), allocatable :: req_header(:) logical :: ok = .true. original_content = '{"id":101,"title":"BMW","description":"A luxurious and high-performance vehicle"}' - req_header = [header_type('Content-Type', 'application/json')] + req_header = [pair_type('Content-Type', 'application/json')] json_data = '{"title":"BMW","description":"A luxurious and high-performance vehicle"}' From 6358fe51dabcdb0086e524ceb690212014ca6e6b Mon Sep 17 00:00:00 2001 From: Rajkumar Date: Sun, 2 Jul 2023 13:37:03 +0530 Subject: [PATCH 2/7] Add functionality for sending files via HTTP POST requests. --- data/file.txt | 1 + example/post_file.f90 | 23 ++++++++++ src/http/http_client.f90 | 97 ++++++++++++++++++++++++++++----------- src/http/http_request.f90 | 2 + 4 files changed, 97 insertions(+), 26 deletions(-) create mode 100644 data/file.txt create mode 100644 example/post_file.f90 diff --git a/data/file.txt b/data/file.txt new file mode 100644 index 0000000..42a0fa9 --- /dev/null +++ b/data/file.txt @@ -0,0 +1 @@ +This is file data to be send to server \ No newline at end of file diff --git a/example/post_file.f90 b/example/post_file.f90 new file mode 100644 index 0000000..bfdeed6 --- /dev/null +++ b/example/post_file.f90 @@ -0,0 +1,23 @@ +program post_file + + ! This program demonstrates sending File using POST request. + + use http, only : request, response_type, HTTP_POST, pair_type + implicit none + type(response_type) :: response + type(pair_type) :: file_data + + ! pair_type('', '/path/to/file.txt') + file_data = pair_type('info', './data/file.txt') + + response = request(url='https://httpbin.org/post', method=HTTP_POST, file=file_data) + + if(.not. response%ok) then + print *,'Error message : ', response%err_msg + else + print *, 'Response Code : ', response%status_code + print *, 'Response Length : ', response%content_length + print *, 'Response Method : ', response%method + print *, 'Response Content : ', response%content + end if +end program post_file \ No newline at end of file diff --git a/src/http/http_client.f90 b/src/http/http_client.f90 index 2f83963..6d280e1 100644 --- a/src/http/http_client.f90 +++ b/src/http/http_client.f90 @@ -10,19 +10,20 @@ module http_client use iso_fortran_env, only: int64 use iso_c_binding, only: c_associated, c_f_pointer, c_funloc, c_loc, & - c_null_ptr, c_ptr, c_size_t + c_null_ptr, c_ptr, c_size_t, c_null_char use curl, only: c_f_str_ptr, curl_easy_cleanup, curl_easy_getinfo, & - curl_easy_init, curl_easy_perform, curl_easy_setopt, & - curl_easy_strerror, curl_slist_append, CURLE_OK, & - CURLINFO_RESPONSE_CODE, CURLOPT_CUSTOMREQUEST, CURLOPT_HEADERDATA, & - CURLOPT_HEADERFUNCTION, CURLOPT_HTTPHEADER, CURLOPT_URL, & - CURLOPT_WRITEDATA, CURLOPT_WRITEFUNCTION, & - CURLOPT_POSTFIELDS, CURLOPT_POSTFIELDSIZE_LARGE, curl_easy_escape + curl_easy_init, curl_easy_perform, curl_easy_setopt, & + curl_easy_strerror, curl_slist_append, CURLE_OK, & + CURLINFO_RESPONSE_CODE, CURLOPT_CUSTOMREQUEST, CURLOPT_HEADERDATA, & + CURLOPT_HEADERFUNCTION, CURLOPT_HTTPHEADER, CURLOPT_URL, & + CURLOPT_WRITEDATA, CURLOPT_WRITEFUNCTION, & + CURLOPT_POSTFIELDS, CURLOPT_POSTFIELDSIZE_LARGE, curl_easy_escape, & + curl_mime_init, curl_mime_addpart, curl_mime_filedata,curl_mime_name, & + CURLOPT_MIMEPOST,curl_mime_data use stdlib_optval, only: optval use http_request, only: request_type use http_response, only: response_type use http_pair, only: append_pair, pair_has_name, pair_type - ! use http_form, only: pair_type implicit none @@ -52,7 +53,7 @@ module http_client ! new client_type object using the request object as a parameter and sends the request to the server ! using the client_get_response method. The function returns the response_type object containing the ! server's response. - function new_request(url, method, header, data, form) result(response) + function new_request(url, method, header, data, form, file) result(response) !! This function creates a new HTTP request object of the request_type type and sends !! the request to the server using the client_type object. The function takes the URL, !! HTTP method, request headers, request data, and form data as input arguments and returns @@ -68,6 +69,8 @@ function new_request(url, method, header, data, form) result(response) !! An optional array of pair_type objects that specifies the request headers to send to the server. type(pair_type), intent(in), optional :: form(:) !! An optional array of pair_type objects that specifies the form data to send in the request body. + type(pair_type), intent(in), optional :: file + !! An optional pair_type object that specifies the file data to send in the request body. type(response_type) :: response !! A response_type object containing the server's response. type(request_type) :: request @@ -101,6 +104,11 @@ function new_request(url, method, header, data, form) result(response) request%form = form end if + ! setting request file + if(present(file)) then + request%file = file + end if + ! Populates the response client = client_type(request=request) response = client%client_get_response() @@ -156,7 +164,8 @@ & function failed. This can occur due to insufficient memory available in the sy call prepare_form_encoded_str(curl_ptr, this%request) ! setting request body - rc = set_body(curl_ptr, this%request%data, this%request%form_encoded_str) + rc = set_body(curl_ptr, this%request%header, this%request%data, & + this%request%form_encoded_str, this%request%form, this%request%file) ! prepare headers for curl call prepare_request_header_ptr(header_list_ptr, this%request%header) @@ -198,9 +207,6 @@ end function client_get_response ! The encoded name-value pairs are concatenated into a single string, separated ! by '&' characters. The resulting string is stored in the form_encoded_str field ! of the request object. - ! Finally, the subroutine checks if the Content-Type header is already set in the - ! request object. If not, it sets the header to application/x-www-form-urlencoded, - ! indicating that the HTTP request body contains URL-encoded form data. subroutine prepare_form_encoded_str(curl_ptr, request) !! This subroutine converts the request%form data into URL-encoded name-value pairs !! and stores the result in the request%form_encoded_str variable. The resulting @@ -225,10 +231,6 @@ subroutine prepare_form_encoded_str(curl_ptr, request) // '=' // curl_easy_escape(curl_ptr, request%form(i)%value, len(request%form(i)%value)) end if end do - ! setting the Content-Type header to application/x-www-form-urlencoded, used for sending form data - if (.not. pair_has_name(request%header, 'Content-Type')) then - call append_pair(request%header, 'Content-Type', 'application/x-www-form-urlencoded') - end if end if end subroutine prepare_form_encoded_str @@ -290,31 +292,74 @@ function set_method(curl_ptr, method, response) result(status) end select end function set_method - ! This function sets the request body for a curl handle based on the input data and form_encoded_str - ! strings and returns the status of the curl_easy_setopt function call. The function takes two input - ! arguments, data and form_encoded_str, which represent the request body data as a raw string or in - ! URL encoded form, respectively. The function then uses an if statement to set the request body using - ! the curl_easy_setopt function call and the CURLOPT_POSTFIELDS and CURLOPT_POSTFIELDSIZE_LARGE options. - ! The function returns an integer value representing the status of the curl_easy_setopt function call. - function set_body(curl_ptr, data, form_encoded_str) result(status) - !! This function sets the request body for a curl handle based on the input data and form_encoded_str - !! strings and returns the status of the curl_easy_setopt function call. + ! The set_body function determines the type of data to include in the request body + ! based on the inputs provided. If data is provided, it is sent as the body of the + ! request. If form is provided without a file, the form data is URL encoded and sent + ! as the body of the request. If file is provided without form, the file is sent + ! using a multipart/form-data header. If both form and file are provided, the file + ! takes priority and the form data along with file is sent as part of the multipart/form-data + ! body. If data, form, and file are all provided, only data is sent and the form and file + ! inputs are ignored. + ! + ! data -> data + ! form -> form + ! file -> file + ! data + form + file -> data + ! form + file -> form + file (in multipart/form-data) + ! + ! Note : At a time only one file can be send + function set_body(curl_ptr, header, data, form_encoded_str, form, file) result(status) + !! The set_body function sets the request body for a curl handle based on the input data and + !! form_encoded_str strings and returns the status of the curl_easy_setopt function call. type(c_ptr), intent(out) :: curl_ptr !! An out argument of type c_ptr that is set to point to a new curl handle. character(*), intent(in), target :: data !! An in argument of type character(*) that specifies the request body data. character(*), intent(in), target :: form_encoded_str !! An in argument of type character(*) that specifies the request body data in URL encoded form. + type(pair_type), allocatable, intent(inout) :: header(:) + !! An allocatable array of header_type objects that specifies the request headers to send to the server. + type(pair_type), allocatable, intent(in) :: form(:) + !! An allocatable array of form_type objects that specifies the form data to send in the request body. + type(pair_type), intent(in) :: file + !! An in argument of type pair_type that specifies the file data to send in the request body. integer :: status !! An integer value representing the status of the curl_easy_setopt function call. integer :: i + integer(kind=8) :: CURL_ZERO_TERMINATED = -1 + type(c_ptr) :: mime_ptr, part_ptr + ! if only data is passed if(len(data) > 0) then status = curl_easy_setopt(curl_ptr, CURLOPT_POSTFIELDS, c_loc(data)) status = curl_easy_setopt(curl_ptr, CURLOPT_POSTFIELDSIZE_LARGE, len(data, kind=int64)) + ! if file is passsed + else if(len(file%name) > 0) then + mime_ptr = curl_mime_init(curl_ptr) + part_ptr = curl_mime_addpart(mime_ptr) + status = curl_mime_filedata(part_ptr, file%value) + status = curl_mime_name(part_ptr, file%name) + ! if both file and form are passed + if(len(form_encoded_str) > 0) then + do i=1, size(form) + part_ptr = curl_mime_addpart(mime_ptr) + status = curl_mime_data(part_ptr, form(i)%value, CURL_ZERO_TERMINATED) + status = curl_mime_name(part_ptr, form(i)%name) + end do + end if + status = curl_easy_setopt(curl_ptr, CURLOPT_MIMEPOST, mime_ptr) + ! setting the Content-Type header to multipart/form-data, used for sending binary data + if (.not. pair_has_name(header, 'Content-Type')) then + call append_pair(header, 'Content-Type', 'multipart/form-data') + end if + ! if only form is passed else if(len(form_encoded_str) > 0) then status = curl_easy_setopt(curl_ptr, CURLOPT_POSTFIELDS, c_loc(form_encoded_str)) status = curl_easy_setopt(curl_ptr, CURLOPT_POSTFIELDSIZE_LARGE, len(form_encoded_str, kind=int64)) + ! setting the Content-Type header to application/x-www-form-urlencoded, used for sending form data + if (.not. pair_has_name(header, 'Content-Type')) then + call append_pair(header, 'Content-Type', 'application/x-www-form-urlencoded') + end if end if end function set_body diff --git a/src/http/http_request.f90 b/src/http/http_request.f90 index bf1c198..575e563 100644 --- a/src/http/http_request.f90 +++ b/src/http/http_request.f90 @@ -36,5 +36,7 @@ module http_request !! An Array of request headers. type(pair_type), allocatable :: form(:) !! An array of fields in an HTTP form. + type(pair_type) :: file + !! Used to store information about files to be sent in HTTP requests. end type request_type end module http_request From 6a295897556712351263c203d463772d957c9a03 Mon Sep 17 00:00:00 2001 From: Rajkumar Date: Sun, 2 Jul 2023 14:37:33 +0530 Subject: [PATCH 3/7] Refactor the set_body procedure --- data/file.txt | 2 +- src/http/http_client.f90 | 92 ++++++++++++++++++++++------------------ 2 files changed, 51 insertions(+), 43 deletions(-) diff --git a/data/file.txt b/data/file.txt index 42a0fa9..32d6490 100644 --- a/data/file.txt +++ b/data/file.txt @@ -1 +1 @@ -This is file data to be send to server \ No newline at end of file +Here is the data from the file that needs to be sent to the server. \ No newline at end of file diff --git a/src/http/http_client.f90 b/src/http/http_client.f90 index 6d280e1..a286ad8 100644 --- a/src/http/http_client.f90 +++ b/src/http/http_client.f90 @@ -19,7 +19,7 @@ module http_client CURLOPT_WRITEDATA, CURLOPT_WRITEFUNCTION, & CURLOPT_POSTFIELDS, CURLOPT_POSTFIELDSIZE_LARGE, curl_easy_escape, & curl_mime_init, curl_mime_addpart, curl_mime_filedata,curl_mime_name, & - CURLOPT_MIMEPOST,curl_mime_data + CURLOPT_MIMEPOST,curl_mime_data, CURL_ZERO_TERMINATED use stdlib_optval, only: optval use http_request, only: request_type use http_response, only: response_type @@ -160,12 +160,8 @@ & function failed. This can occur due to insufficient memory available in the sy ! setting request method rc = set_method(curl_ptr, this%request%method, response) - ! encode the request form - call prepare_form_encoded_str(curl_ptr, this%request) - ! setting request body - rc = set_body(curl_ptr, this%request%header, this%request%data, & - this%request%form_encoded_str, this%request%form, this%request%file) + rc = set_body(curl_ptr, this%request) ! prepare headers for curl call prepare_request_header_ptr(header_list_ptr, this%request%header) @@ -207,7 +203,7 @@ end function client_get_response ! The encoded name-value pairs are concatenated into a single string, separated ! by '&' characters. The resulting string is stored in the form_encoded_str field ! of the request object. - subroutine prepare_form_encoded_str(curl_ptr, request) + function prepare_form_encoded_str(curl_ptr, request) result(form_encoded_str) !! This subroutine converts the request%form data into URL-encoded name-value pairs !! and stores the result in the request%form_encoded_str variable. The resulting !! string is used as the HTTP request body with the application/x-www-form-urlencoded @@ -218,21 +214,22 @@ subroutine prepare_form_encoded_str(curl_ptr, request) type(request_type), intent(inout) :: request !! An inout argument of the request_type type, which contains the form data to be !! encoded and the form_encoded_str variable to store the result. + character(:), allocatable :: form_encoded_str integer :: i if(allocated(request%form)) then do i=1, size(request%form) - if(.not. allocated(request%form_encoded_str)) then - request%form_encoded_str = curl_easy_escape(curl_ptr, request%form(i)%name, & + if(.not. allocated(form_encoded_str)) then + form_encoded_str = curl_easy_escape(curl_ptr, request%form(i)%name, & len(request%form(i)%name)) // '=' // curl_easy_escape(curl_ptr, & request%form(i)%value, len(request%form(i)%value)) else - request%form_encoded_str = request%form_encoded_str // '&' // & + form_encoded_str = form_encoded_str // '&' // & curl_easy_escape(curl_ptr, request%form(i)%name, len(request%form(i)%name))& // '=' // curl_easy_escape(curl_ptr, request%form(i)%value, len(request%form(i)%value)) end if end do end if - end subroutine prepare_form_encoded_str + end function prepare_form_encoded_str ! This subroutine prepares a linked list of headers for an HTTP request using the libcurl library. ! The function takes an array of pair_type objects that contain the key-value pairs of the headers @@ -308,61 +305,72 @@ end function set_method ! form + file -> form + file (in multipart/form-data) ! ! Note : At a time only one file can be send - function set_body(curl_ptr, header, data, form_encoded_str, form, file) result(status) - !! The set_body function sets the request body for a curl handle based on the input data and - !! form_encoded_str strings and returns the status of the curl_easy_setopt function call. + function set_body(curl_ptr, request) result(status) + !! The set_body function sets the request body. type(c_ptr), intent(out) :: curl_ptr !! An out argument of type c_ptr that is set to point to a new curl handle. - character(*), intent(in), target :: data - !! An in argument of type character(*) that specifies the request body data. - character(*), intent(in), target :: form_encoded_str - !! An in argument of type character(*) that specifies the request body data in URL encoded form. - type(pair_type), allocatable, intent(inout) :: header(:) - !! An allocatable array of header_type objects that specifies the request headers to send to the server. - type(pair_type), allocatable, intent(in) :: form(:) - !! An allocatable array of form_type objects that specifies the form data to send in the request body. - type(pair_type), intent(in) :: file - !! An in argument of type pair_type that specifies the file data to send in the request body. + type(request_type), intent(inout) :: request + !! The HTTP request integer :: status !! An integer value representing the status of the curl_easy_setopt function call. + integer :: i - integer(kind=8) :: CURL_ZERO_TERMINATED = -1 + character(len=:), allocatable :: form_encoded_str type(c_ptr) :: mime_ptr, part_ptr ! if only data is passed - if(len(data) > 0) then - status = curl_easy_setopt(curl_ptr, CURLOPT_POSTFIELDS, c_loc(data)) - status = curl_easy_setopt(curl_ptr, CURLOPT_POSTFIELDSIZE_LARGE, len(data, kind=int64)) + if(len(request%data) > 0) then + status = set_postfields(curl_ptr, request%data) + ! if file is passsed - else if(len(file%name) > 0) then + else if(len(request%file%name) > 0) then mime_ptr = curl_mime_init(curl_ptr) part_ptr = curl_mime_addpart(mime_ptr) - status = curl_mime_filedata(part_ptr, file%value) - status = curl_mime_name(part_ptr, file%name) + status = curl_mime_filedata(part_ptr, request%file%value) + status = curl_mime_name(part_ptr, request%file%name) + ! if both file and form are passed - if(len(form_encoded_str) > 0) then - do i=1, size(form) + if(allocated(request%form)) then + do i=1, size(request%form) part_ptr = curl_mime_addpart(mime_ptr) - status = curl_mime_data(part_ptr, form(i)%value, CURL_ZERO_TERMINATED) - status = curl_mime_name(part_ptr, form(i)%name) + status = curl_mime_data(part_ptr, request%form(i)%value, CURL_ZERO_TERMINATED) + status = curl_mime_name(part_ptr, request%form(i)%name) end do end if status = curl_easy_setopt(curl_ptr, CURLOPT_MIMEPOST, mime_ptr) + ! setting the Content-Type header to multipart/form-data, used for sending binary data - if (.not. pair_has_name(header, 'Content-Type')) then - call append_pair(header, 'Content-Type', 'multipart/form-data') + if (.not. pair_has_name(request%header, 'Content-Type')) then + call append_pair(request%header, 'Content-Type', 'multipart/form-data') end if + ! if only form is passed - else if(len(form_encoded_str) > 0) then - status = curl_easy_setopt(curl_ptr, CURLOPT_POSTFIELDS, c_loc(form_encoded_str)) - status = curl_easy_setopt(curl_ptr, CURLOPT_POSTFIELDSIZE_LARGE, len(form_encoded_str, kind=int64)) + else if(allocated(request%form)) then + request%form_encoded_str = prepare_form_encoded_str(curl_ptr, request) + status = set_postfields(curl_ptr, request%form_encoded_str) + ! setting the Content-Type header to application/x-www-form-urlencoded, used for sending form data - if (.not. pair_has_name(header, 'Content-Type')) then - call append_pair(header, 'Content-Type', 'application/x-www-form-urlencoded') + if (.not. pair_has_name(request%header, 'Content-Type')) then + call append_pair(request%header, 'Content-Type', 'application/x-www-form-urlencoded') end if end if + end function set_body + function set_postfields(curl_ptr, data) result(status) + !! Set the data to be sent in the HTTP POST request body. + type(c_ptr), intent(inout) :: curl_ptr + !! Pointer to the CURL handle. + character(*), intent(in), target :: data + !! The data to be sent in the request body. + integer :: status + !! An integer indicating whether the operation was successful (0) or not (non-zero). + + status = curl_easy_setopt(curl_ptr, CURLOPT_POSTFIELDS, c_loc(data)) + status = curl_easy_setopt(curl_ptr, CURLOPT_POSTFIELDSIZE_LARGE, len(data, kind=int64)) + + end function set_postfields + ! This function is a callback function used by the libcurl library to handle HTTP responses. It is ! called for each chunk of data received from the server and appends the data to a response_type object. ! The function takes four input arguments: ptr, size, nmemb, and client_data. ptr is a pointer to the From 2da5256ea194213e2787b66f49a85df84e17f976 Mon Sep 17 00:00:00 2001 From: Rajkumar Date: Wed, 5 Jul 2023 14:41:15 +0530 Subject: [PATCH 4/7] add HTTP_PUT --- src/http.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/http.f90 b/src/http.f90 index a2792a9..78a3e3f 100644 --- a/src/http.f90 +++ b/src/http.f90 @@ -1,6 +1,6 @@ module http use http_request, only: & - HTTP_DELETE, HTTP_GET, HTTP_HEAD, HTTP_PATCH, HTTP_POST, HTTP_POST + HTTP_DELETE, HTTP_GET, HTTP_HEAD, HTTP_PATCH, HTTP_POST, HTTP_PUT use http_response, only: response_type use http_client, only: request use http_pair, only : pair_type From de1b62d66c39f93cd54f7ff14ac9d4ae9647fed8 Mon Sep 17 00:00:00 2001 From: milancurcic Date: Wed, 5 Jul 2023 10:29:47 -0400 Subject: [PATCH 5/7] Edit docstrings --- src/http/http_pair.f90 | 61 +++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/src/http/http_pair.f90 b/src/http/http_pair.f90 index 0588d67..b624c35 100644 --- a/src/http/http_pair.f90 +++ b/src/http/http_pair.f90 @@ -1,9 +1,9 @@ module http_pair - !! This module provides a simple name-value type to use for HTTP Header, Form, File. - !! It also provides procedures to inquire about the presence of a name and - !! its value in a pair array, as well as a procedure to append new - !! pairs to an existing array of pairs. + !! This module provides a simple name-value type to use for HTTP Header, + !! Form, and File. It also provides procedures to inquire about the presence + !! of a name and its value in a pair array, as well as a procedure to append + !! new pairs to an existing array of pairs. use stdlib_ascii, only: to_lower @@ -16,36 +16,34 @@ module http_pair public :: pair_has_name type :: pair_type - !! A derived type use to store a name-value pair, it is - !! use in many instances like : - !! 1. To store request and response headers. - !! * `name` represent the header name. - !! * `value` represent the header value. - !! 2. While sending Form data, it represent a single a single field - !! of an HTTP form. - !! * `name` represents the name of the single form field. - !! * `value` represents the value of that single form field. - !! 3. While sending File, it is used to store information about - !! files to be sent. - !! * `name` represent the name of the file. - !! * `value` represent the path of the file on the local system. + !! A derived type use to store a name-value pair, it is used in many + !! instances like: + !! 1. Storing request and response headers: + !! * `name` to represent the header name. + !! * `value` to represent the header value. + !! 2. Representing fields in a url-encoded HTTP form: + !! * `name` to represent the form field name. + !! * `value` to represent the form field value. + !! 3. Storing information about the file to upload: + !! * `name` to represent the name of the file. + !! * `value` to represent the path of the file on the local system. character(:), allocatable :: name - !! represent the name of the pair. + !! Name (key) character(:), allocatable :: value - !! represent the value of the pair. + !! Value end type pair_type contains subroutine append_pair(pair, name, value) - !! Append a new pair_type instance with name and value members to + !! Append a new `pair_type` instance with name and value members to !! the pair array. type(pair_type), allocatable, intent(inout) :: pair(:) - !! pair array to append to + !! `pair_type` array to append to character(*), intent(in) :: name - !! name member of pair_type to append + !! Name member of `pair_type` to append character(*), intent(in) :: value - !! Value member of pair_type to append + !! Value member of `pair_type` to append type(pair_type), allocatable :: temp(:) integer :: n @@ -62,14 +60,15 @@ subroutine append_pair(pair, name, value) end subroutine append_pair pure function get_pair_value(pair, name) result(val) - !! Return the value of a requested name in a pair array. If the name is - !! not found, the function returns an empty string (unallocated). If - !! there are duplicates of the name in the pair array, return the value - !! of the first occurence of the name. + !! Return the value of a requested name in a pair array. + !! The search for the pair by its name is case-insensitive. + !! If the name is not found, the function returns an unallocated string. + !! If there are duplicates of the name in the pair array, return the + !! value of the first occurence of the name. type(pair_type), intent(in) :: pair(:) - !! pair to search for name + !! Pair to search for name character(*), intent(in) :: name - !! name to search in pair + !! Name to search in pair character(:), allocatable :: val !! Value of the name to return integer :: n @@ -88,9 +87,9 @@ pure logical function pair_has_name(pair, name) !! HTTP pairs are case insensitive, so values are converted to !! lowercase before comparison. type(pair_type), intent(in) :: pair(:) - !! pair to search for name + !! Pair to search for name character(*), intent(in) :: name - !! name to search in pair + !! Name to search in pair integer :: n pair_has_name = .false. From 9a922e5bc1b665339eaf5fc019919f028a705fe0 Mon Sep 17 00:00:00 2001 From: milancurcic Date: Wed, 5 Jul 2023 10:29:55 -0400 Subject: [PATCH 6/7] Indentation --- src/http/http_client.f90 | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/http/http_client.f90 b/src/http/http_client.f90 index a286ad8..fd7cbe8 100644 --- a/src/http/http_client.f90 +++ b/src/http/http_client.f90 @@ -12,14 +12,14 @@ module http_client use iso_c_binding, only: c_associated, c_f_pointer, c_funloc, c_loc, & c_null_ptr, c_ptr, c_size_t, c_null_char use curl, only: c_f_str_ptr, curl_easy_cleanup, curl_easy_getinfo, & - curl_easy_init, curl_easy_perform, curl_easy_setopt, & - curl_easy_strerror, curl_slist_append, CURLE_OK, & - CURLINFO_RESPONSE_CODE, CURLOPT_CUSTOMREQUEST, CURLOPT_HEADERDATA, & - CURLOPT_HEADERFUNCTION, CURLOPT_HTTPHEADER, CURLOPT_URL, & - CURLOPT_WRITEDATA, CURLOPT_WRITEFUNCTION, & - CURLOPT_POSTFIELDS, CURLOPT_POSTFIELDSIZE_LARGE, curl_easy_escape, & - curl_mime_init, curl_mime_addpart, curl_mime_filedata,curl_mime_name, & - CURLOPT_MIMEPOST,curl_mime_data, CURL_ZERO_TERMINATED + curl_easy_init, curl_easy_perform, curl_easy_setopt, & + curl_easy_strerror, curl_slist_append, CURLE_OK, & + CURLINFO_RESPONSE_CODE, CURLOPT_CUSTOMREQUEST, CURLOPT_HEADERDATA, & + CURLOPT_HEADERFUNCTION, CURLOPT_HTTPHEADER, CURLOPT_URL, & + CURLOPT_WRITEDATA, CURLOPT_WRITEFUNCTION, & + CURLOPT_POSTFIELDS, CURLOPT_POSTFIELDSIZE_LARGE, curl_easy_escape, & + curl_mime_init, curl_mime_addpart, curl_mime_filedata,curl_mime_name, & + CURLOPT_MIMEPOST,curl_mime_data, CURL_ZERO_TERMINATED use stdlib_optval, only: optval use http_request, only: request_type use http_response, only: response_type From efaa215bd581b3da580e21c0261717d39b8b70e4 Mon Sep 17 00:00:00 2001 From: milancurcic Date: Wed, 5 Jul 2023 10:31:45 -0400 Subject: [PATCH 7/7] Move data/file.txt in example/ --- {data => example/data}/file.txt | 0 example/post_file.f90 | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename {data => example/data}/file.txt (100%) diff --git a/data/file.txt b/example/data/file.txt similarity index 100% rename from data/file.txt rename to example/data/file.txt diff --git a/example/post_file.f90 b/example/post_file.f90 index bfdeed6..c8d4e66 100644 --- a/example/post_file.f90 +++ b/example/post_file.f90 @@ -8,7 +8,7 @@ program post_file type(pair_type) :: file_data ! pair_type('', '/path/to/file.txt') - file_data = pair_type('info', './data/file.txt') + file_data = pair_type('file.txt', './example/data/file.txt') response = request(url='https://httpbin.org/post', method=HTTP_POST, file=file_data)