Skip to content

Commit 1713708

Browse files
Replace hashmap with header_type array to store request and response headers (#23)
* Replace hashmap with header_type array to store request and response headers * Remove unused import * Explicit import * Use optval for optional args default values * remove set_default_headers * migrate append_header procedure to http_response.f90 * Using move_alloc() to append response header * Explicit imports * Remove no longer used append_header --------- Co-authored-by: milancurcic <caomaco@gmail.com>
1 parent d724044 commit 1713708

File tree

8 files changed

+111
-155
lines changed

8 files changed

+111
-155
lines changed

example/response_header.f90

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,31 @@
11
program response_header
2+
! This program demonstrates sending user-provided headers in a GET request
3+
! and iterating over the headers of the response sent back by the server.
24
use stdlib_string_type, only: string_type, write(formatted)
35
use http, only: response_type, request, header_type
6+
47
implicit none
58
type(response_type) :: response
6-
type(string_type), allocatable :: header_keys(:)
7-
type(header_type) :: req_header
9+
type(header_type), allocatable :: header(:), req_header(:)
810
character(:), allocatable :: val
911
integer :: i = 0
1012

11-
! setting request header
12-
call req_header%set('h1', 'v1')
13-
call req_header%set('h2', 'v2')
14-
call req_header%set('h3', 'v3')
15-
call req_header%set('h4', 'v4')
13+
req_header = [ &
14+
header_type('Another-One', 'Hello'), &
15+
header_type('Set-Cookie', 'Theme-Light'), &
16+
header_type('Set-Cookie', 'Auth-Token: 12345'), &
17+
header_type('User-Agent', 'my user agent') &
18+
]
1619

17-
response = request(url='https://gorest.co.in/public/v2/todos/15726', header=req_header)
18-
if(.not. response%ok) then
20+
response = request(url='https://reqres.in/api/users/1', header=req_header)
21+
22+
if (.not. response%ok) then
1923
print *,'Error message : ', response%err_msg
2024
else
21-
print *, '=========== Response header value by passing string_type ============'
22-
header_keys = response%header%keys()
23-
do i = 1, size(header_keys)
24-
val = response%header%value(header_keys(i))
25-
print *, header_keys(i), ': ', val
25+
header = response%header
26+
! Iterate over response headers.
27+
do i = 1, size(header)
28+
print *, header(i)%key, ': ', header(i)%value
2629
end do
27-
print *, '=========== Response header value by passing characters ============'
28-
val = response%header%value('date')
29-
print *, 'date', ' : ',val
3030
end if
31-
end program response_header
31+
end program response_header

example/simple_get.f90

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
program simple_get
2-
use http, only : response_type, request
2+
! This program demonstrates sending a simple GET request and printing the
3+
! status, length of the body, method, and the body of the response.
4+
use http, only: response_type, request
35
implicit none
46
type(response_type) :: response
57

@@ -13,4 +15,4 @@ program simple_get
1315
print *, 'Response Content : ', response%content
1416
end if
1517

16-
end program simple_get
18+
end program simple_get

fpm.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,4 @@ source-form = "free"
2020

2121
[dependencies]
2222
fortran-curl = {git = "https://github.com/interkosmos/fortran-curl.git"}
23-
fhash = { git = "https://github.com/LKedward/fhash.git" }
2423
stdlib = "*"

src/http/http_client.f90

Lines changed: 40 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
module http_client
2-
use iso_c_binding
3-
use curl
4-
use stdlib_string_type
5-
use fhash, only: key => fhash_key
2+
use iso_c_binding, only: c_associated, c_f_pointer, c_funloc, c_loc, &
3+
c_null_char, c_null_ptr, c_ptr, c_size_t
4+
use curl, only: c_f_str_ptr, curl_easy_cleanup, curl_easy_getinfo, &
5+
curl_easy_init, curl_easy_perform, curl_easy_setopt, &
6+
curl_easy_strerror, curl_slist_append, CURLE_OK, &
7+
CURLINFO_RESPONSE_CODE, CURLOPT_CUSTOMREQUEST, CURLOPT_HEADERDATA, &
8+
CURLOPT_HEADERFUNCTION, CURLOPT_HTTPHEADER, CURLOPT_URL, &
9+
CURLOPT_WRITEDATA, CURLOPT_WRITEFUNCTION
10+
use stdlib_optval, only: optval
11+
use stdlib_string_type, only: string_type, to_lower, operator(==)
612
use http_request, only: request_type
713
use http_response, only: response_type
814
use http_header, only : header_type
@@ -32,42 +38,35 @@ module http_client
3238
function new_request(url, method, header) result(response)
3339
character(len=*), intent(in) :: url
3440
integer, intent(in), optional :: method
35-
type(header_type), intent(inout), optional :: header
36-
type(header_type) :: default_header
41+
type(header_type), intent(in), optional :: header(:)
3742
type(request_type) :: request
3843
type(response_type) :: response
3944
type(client_type) :: client
4045

41-
! setting default HTTP method
42-
if(present(method)) then
43-
request%method = method
44-
else
45-
request%method = 1
46-
end if
46+
! Set default HTTP method.
47+
request%method = optval(method, 1)
4748

48-
! setting defautl request headers
49+
! Set default request headers.
50+
request%header = [header_type('user-agent', 'fortran-http/1.0.0')]
4951
if(present(header)) then
50-
call set_default_headers(header)
51-
request%header = header
52-
else
53-
call set_default_headers(default_header)
54-
request%header = default_header
52+
request%header = [header, request%header]
5553
end if
56-
! populate the request header_key array
57-
call request%header%set_header_key()
54+
5855
! setting request url
5956
request%url = url
57+
6058
client = client_type(request=request)
61-
response = client%client_get_response()
6259

60+
! Populates the response
61+
response = client%client_get_response()
6362
end function new_request
63+
6464
! Constructor for client_type type.
6565
function new_client(request) result(client)
6666
type(request_type), intent(in) :: request
6767
type(client_type) :: client
6868

6969
client%request = request
70-
7170
end function new_client
7271

7372
function client_get_response(this) result(response)
@@ -79,11 +78,10 @@ function client_get_response(this) result(response)
7978
curl_ptr = c_null_ptr
8079
header_list_ptr = c_null_ptr
8180

82-
! logic for populating response using fortran-curl
8381
response%url = this%request%url
8482

8583
! prepare headers for curl
86-
call prepare_request_header_ptr(header_list_ptr, this%request)
84+
call prepare_request_header_ptr(header_list_ptr, this%request%header)
8785

8886
curl_ptr = curl_easy_init()
8987

@@ -94,20 +92,28 @@ & function failed. This can occur due to insufficient memory available in the sy
9492
& Additionally, if libcurl is not installed or configured properly on the system"
9593
return
9694
end if
95+
9796
! setting request URL
9897
rc = curl_easy_setopt(curl_ptr, CURLOPT_URL, this%request%url // c_null_char)
98+
9999
! setting request method
100100
rc = client_set_method(curl_ptr, this%request%method, response)
101+
101102
! setting request header
102103
rc = curl_easy_setopt(curl_ptr, CURLOPT_HTTPHEADER, header_list_ptr);
104+
103105
! setting callback for writing received data
104106
rc = curl_easy_setopt(curl_ptr, CURLOPT_WRITEFUNCTION, c_funloc(client_response_callback))
107+
105108
! setting response content pointer to write callback
106109
rc = curl_easy_setopt(curl_ptr, CURLOPT_WRITEDATA, c_loc(response))
110+
107111
! setting callback for writing received headers
108112
rc = curl_easy_setopt(curl_ptr, CURLOPT_HEADERFUNCTION, c_funloc(client_header_callback))
113+
109114
! setting response header pointer to write callback
110115
rc = curl_easy_setopt(curl_ptr, CURLOPT_HEADERDATA, c_loc(response))
116+
111117
! Send request.
112118
rc = curl_easy_perform(curl_ptr)
113119

@@ -119,35 +125,24 @@ & function failed. This can occur due to insufficient memory available in the sy
119125
! setting response status_code
120126
rc = curl_easy_getinfo(curl_ptr, CURLINFO_RESPONSE_CODE, response%status_code)
121127

122-
! populate the response header_key array
123-
call response%header%set_header_key()
124-
125128
call curl_easy_cleanup(curl_ptr)
126129

127130
end function client_get_response
128131

129-
subroutine prepare_request_header_ptr(header_list_ptr, request)
130-
class(request_type), intent(in) :: request
132+
subroutine prepare_request_header_ptr(header_list_ptr, req_headers)
131133
type(c_ptr), intent(out) :: header_list_ptr
132-
type(string_type), allocatable :: req_headers(:)
134+
type(header_type), allocatable, intent(in) :: req_headers(:)
133135
character(:), allocatable :: h_key, h_val, final_header_string
134136
integer :: i
135137

136-
req_headers = request%header%keys()
137138
do i = 1, size(req_headers)
138-
h_key = char(req_headers(i))
139-
h_val = request%header%value(req_headers(i))
140-
139+
h_key = req_headers(i)%key
140+
h_val = req_headers(i)%value
141141
final_header_string = h_key // ':' // h_val // c_null_char
142142
header_list_ptr = curl_slist_append(header_list_ptr, final_header_string)
143143
end do
144144
end subroutine prepare_request_header_ptr
145145

146-
subroutine set_default_headers(header)
147-
type(header_type), intent(inout) :: header
148-
call header%set('User-Agent', 'fortran-http/1.0.0')
149-
end subroutine set_default_headers
150-
151146
function client_set_method(curl_ptr, method, response) result(status)
152147
type(c_ptr), intent(out) :: curl_ptr
153148
integer, intent(in) :: method
@@ -203,6 +198,7 @@ function client_response_callback(ptr, size, nmemb, client_data) bind(c)
203198
response%content = response%content // buf
204199
deallocate (buf)
205200
response%content_length = response%content_length + nmemb
201+
206202
! Return number of received bytes.
207203
client_response_callback = nmemb
208204

@@ -230,20 +226,23 @@ function client_header_callback(ptr, size, nmemb, client_data) bind(c)
230226
! Convert C pointer to Fortran allocatable character.
231227
call c_f_str_ptr(ptr, buf, nmemb)
232228
if (.not. allocated(buf)) return
229+
233230
! Parsing Header, and storing in hashmap
234231
i = index(buf, ':')
235232
if(i /= 0 .and. len(buf) > 2) then
236233
h_key = trim(buf(:i-1))
237234
h_value = buf(i+2 : )
238235
h_value = h_value( : len(h_value)-2)
239236
if(len(h_value) > 0 .and. len(h_key) > 0) then
240-
call response%header%set(h_key, h_value)
237+
call response%append_header(h_key, h_value)
238+
! response%header = [response%header, header_type(h_key, h_value)]
241239
end if
242240
end if
243241
deallocate(buf)
242+
244243
! Return number of received bytes.
245244
client_header_callback = nmemb
246245

247246
end function client_header_callback
248-
247+
249248
end module http_client

src/http/http_header.f90

Lines changed: 1 addition & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,9 @@
11
module http_header
22
use iso_c_binding
3-
use fhash, only: fhash_tbl_t, key => fhash_key, fhash_iter_t, fhash_key_t
4-
use stdlib_string_type
5-
! use curl
63
implicit none
74
private
85
public :: header_type
96
type :: header_type
10-
type(fhash_tbl_t), private :: header
11-
type(string_type), private, allocatable :: header_key(:)
12-
integer :: header_count = 0
13-
contains
14-
procedure :: set_header_key
15-
procedure :: keys => get_header_keys
16-
procedure :: header_string_type_key
17-
procedure :: header_char_key
18-
generic :: value => header_string_type_key, header_char_key
19-
procedure :: set => update_header
7+
character(:), allocatable :: key, value
208
end type header_type
21-
contains
22-
subroutine update_header(this, h_key, h_val)
23-
class(header_type), intent(inout) :: this
24-
character(*), intent(in) :: h_key, h_val
25-
if(len_trim(h_key) > 0 .and. len_trim(h_val) > 0) then
26-
call this%header%set(key(h_key), value=h_val)
27-
this%header_count = this%header_count + 1
28-
end if
29-
end subroutine update_header
30-
31-
subroutine set_header_key(this)
32-
class(header_type), intent(inout) :: this
33-
type(fhash_iter_t) :: iter
34-
class(fhash_key_t), allocatable :: ikey
35-
class(*), allocatable :: idata
36-
character(:), allocatable :: val
37-
integer :: i
38-
i = 1
39-
allocate(this%header_key(this%header_count))
40-
iter = fhash_iter_t(this%header)
41-
do while(iter%next(ikey,idata) .and. i <= this%header_count)
42-
this%header_key(i) = string_type(trim(ikey%to_string()))
43-
i = i + 1
44-
end do
45-
end subroutine set_header_key
46-
47-
function get_header_keys(this)
48-
class(header_type), intent(in) :: this
49-
type(string_type), allocatable :: get_header_keys(:)
50-
get_header_keys = this%header_key
51-
end function get_header_keys
52-
53-
function header_string_type_key(this, h_key) result(header_value)
54-
class(header_type) :: this
55-
type(string_type) :: h_key
56-
character(:), allocatable :: header_value
57-
if(len(h_key) > 0) then
58-
call this%header%get(key(char(h_key)),header_value)
59-
else
60-
header_value = ''
61-
end if
62-
end function header_string_type_key
63-
64-
function header_char_key(this, h_key) result(header_value)
65-
class(header_type) :: this
66-
character(*) :: h_key
67-
character(:), allocatable :: header_value
68-
if(len(h_key) > 0) then
69-
call this%header%get(key(h_key),header_value)
70-
else
71-
header_value = ''
72-
end if
73-
end function header_char_key
74-
75-
769
end module http_header

src/http/http_request.f90

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
module http_request
2-
use, intrinsic :: iso_c_binding
32
use http_header, only : header_type
43
implicit none
54

@@ -19,7 +18,7 @@ module http_request
1918
type :: request_type
2019
character(len=:), allocatable :: url
2120
integer :: method
22-
type(header_type) :: header
21+
type(header_type), allocatable :: header(:)
2322
end type request_type
2423

2524
end module http_request

src/http/http_response.f90

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,30 @@ module http_response
1212
integer :: status_code = 0
1313
integer(kind=int64) :: content_length = 0
1414
logical :: ok = .true.
15-
type(header_type) :: header
15+
type(header_type), allocatable :: header(:)
16+
contains
17+
procedure :: append_header
1618
end type response_type
1719

20+
contains
21+
subroutine append_header(this, key, value)
22+
class(response_type), intent(inout) :: this
23+
character(*), intent(in) :: key, value
24+
type(header_type), allocatable :: temp(:)
25+
integer :: n
26+
27+
if (allocated(this%header)) then
28+
n = size(this%header)
29+
allocate(temp(n+1))
30+
temp(1:n) = this%header
31+
temp(n+1) = header_type(key, value)
32+
call move_alloc(temp, this%header)
33+
else
34+
allocate(this%header(1))
35+
this%header(1) = header_type(key, value)
36+
end if
37+
38+
end subroutine append_header
39+
40+
1841
end module http_response

0 commit comments

Comments
 (0)