/[eiffelstudio]/branches/eth/eve/Src/contrib/library/network/http_client/src/spec/libcurl/libcurl_http_client_request.e
ViewVC logotype

Contents of /branches/eth/eve/Src/contrib/library/network/http_client/src/spec/libcurl/libcurl_http_client_request.e

Parent Directory Parent Directory | Revision Log Revision Log


Revision 92964 - (show annotations)
Fri Sep 20 05:41:23 2013 UTC (6 years ago) by jasonw
File size: 13829 byte(s)
<<Merged from trunk#92963.>>
1 note
2 description: "[
3 Specific implementation of HTTP_CLIENT_REQUEST based on Eiffel cURL library
4 ]"
5 date: "$Date$"
6 revision: "$Revision$"
7
8 class
9 LIBCURL_HTTP_CLIENT_REQUEST
10
11 inherit
12 HTTP_CLIENT_REQUEST
13 rename
14 make as make_request
15 redefine
16 session
17 end
18
19 create
20 make
21
22 feature {NONE} -- Initialization
23
24 make (a_url: READABLE_STRING_8; a_request_method: like request_method; a_session: like session; ctx: like context)
25 do
26 make_request (a_url, a_session, ctx)
27 request_method := a_request_method
28 apply_workaround
29 end
30
31 apply_workaround
32 -- Due to issue with Eiffel cURL on Windows 32bits
33 -- we need to do the following workaround
34 once
35 if attached (create {INET_ADDRESS_FACTORY}).create_localhost then
36 end
37 end
38
39 session: LIBCURL_HTTP_CLIENT_SESSION
40
41 feature -- Access
42
43 request_method: READABLE_STRING_8
44
45 feature -- Execution
46
47 execute: HTTP_CLIENT_RESPONSE
48 local
49 l_result: INTEGER
50 l_curl_string: detachable CURL_STRING
51 l_url: READABLE_STRING_8
52 l_form: detachable CURL_FORM
53 l_last: CURL_FORM
54 l_upload_file: detachable RAW_FILE
55 l_custom_function: detachable LIBCURL_CUSTOM_FUNCTION
56 curl: detachable CURL_EXTERNALS
57 curl_easy: detachable CURL_EASY_EXTERNALS
58 curl_handle: POINTER
59 ctx: like context
60 p_slist: POINTER
61 retried: BOOLEAN
62 l_form_data: detachable HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32]
63 l_upload_data: detachable READABLE_STRING_8
64 l_upload_filename: detachable READABLE_STRING_GENERAL
65 l_headers: like headers
66 do
67 if not retried then
68 curl := session.curl
69 curl_easy := session.curl_easy
70 curl_handle := curl_easy.init
71 curl.global_init
72
73 ctx := context
74
75 --| Configure cURL session
76 initialize_curl_session (ctx, curl, curl_easy, curl_handle)
77
78 --| URL
79 l_url := url
80 if ctx /= Void then
81 append_parameters_to_url (ctx.query_parameters, l_url)
82 end
83
84 debug ("service")
85 io.put_string ("SERVICE: " + l_url)
86 io.put_new_line
87 end
88 curl_easy.setopt_string (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_url, l_url)
89
90 l_headers := headers
91
92 -- Context
93 if ctx /= Void then
94 --| Credential
95 if ctx.credentials_required then
96 if attached credentials as l_credentials then
97 inspect auth_type_id
98 when {HTTP_CLIENT_CONSTANTS}.Auth_type_none then
99 curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_httpauth, {CURL_OPT_CONSTANTS}.curlauth_none)
100 when {HTTP_CLIENT_CONSTANTS}.Auth_type_basic then
101 curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_httpauth, {CURL_OPT_CONSTANTS}.curlauth_basic)
102 when {HTTP_CLIENT_CONSTANTS}.Auth_type_digest then
103 curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_httpauth, {CURL_OPT_CONSTANTS}.curlauth_digest)
104 when {HTTP_CLIENT_CONSTANTS}.Auth_type_any then
105 curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_httpauth, {CURL_OPT_CONSTANTS}.curlauth_any)
106 when {HTTP_CLIENT_CONSTANTS}.Auth_type_anysafe then
107 curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_httpauth, {CURL_OPT_CONSTANTS}.curlauth_anysafe)
108 else
109 end
110
111 curl_easy.setopt_string (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_userpwd, l_credentials)
112 else
113 --| Credentials not provided ...
114 end
115 end
116
117 if ctx.has_upload_data then
118 l_upload_data := ctx.upload_data
119 end
120 if ctx.has_upload_filename then
121 l_upload_filename := ctx.upload_filename
122 end
123 if ctx.has_form_data then
124 l_form_data := ctx.form_parameters
125 check non_empty_form_data: not l_form_data.is_empty end
126 if l_upload_data = Void and l_upload_filename = Void then
127 -- Send as form-urlencoded
128 if
129 l_headers.has_key ("Content-Type") and then
130 attached l_headers.found_item as l_ct
131 then
132 if l_ct.starts_with ("application/x-www-form-urlencoded") then
133 -- Content-Type is already application/x-www-form-urlencoded
134 l_upload_data := ctx.form_parameters_to_url_encoded_string
135 else
136 -- Existing Content-Type and not application/x-www-form-urlencoded
137 end
138 else
139 l_upload_data := ctx.form_parameters_to_url_encoded_string
140 end
141 else
142 create l_form.make
143 create l_last.make
144 from
145 l_form_data.start
146 until
147 l_form_data.after
148 loop
149 curl.formadd_string_string (l_form, l_last,
150 {CURL_FORM_CONSTANTS}.curlform_copyname, l_form_data.key_for_iteration,
151 {CURL_FORM_CONSTANTS}.curlform_copycontents, l_form_data.item_for_iteration,
152 {CURL_FORM_CONSTANTS}.curlform_end
153 )
154 l_form_data.forth
155 end
156 l_last.release_item
157 curl_easy.setopt_form (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_httppost, l_form)
158 end
159 end
160
161 if l_upload_data /= Void then
162 check
163 post_or_put_request_method: request_method.is_case_insensitive_equal ("POST")
164 or request_method.is_case_insensitive_equal ("PUT")
165 or request_method.is_case_insensitive_equal ("PATCH")
166 end
167
168 curl_easy.setopt_string (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_postfields, l_upload_data)
169 curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_postfieldsize, l_upload_data.count)
170 elseif l_upload_filename /= Void then
171 check
172 post_or_put_request_method: request_method.is_case_insensitive_equal ("POST")
173 or request_method.is_case_insensitive_equal ("PUT")
174 or request_method.is_case_insensitive_equal ("PATCH")
175 end
176
177 create l_upload_file.make_with_name (l_upload_filename)
178 if l_upload_file.exists and then l_upload_file.is_readable then
179 curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_upload, 1)
180
181 curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_infilesize, l_upload_file.count)
182 -- specify callback read function for upload file
183 if l_custom_function = Void then
184 create l_custom_function.make
185 end
186 l_custom_function.set_file_to_read (l_upload_file)
187 l_upload_file.open_read
188 curl_easy.set_curl_function (l_custom_function)
189 end
190 else
191 check no_upload_data: l_upload_data = Void and l_upload_filename = Void end
192 end
193 end -- ctx /= Void
194
195 --| Header
196 across
197 l_headers as curs
198 loop
199 p_slist := curl.slist_append (p_slist, curs.key + ": " + curs.item)
200 end
201 p_slist := curl.slist_append (p_slist, "Expect:")
202 curl_easy.setopt_slist (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_httpheader, p_slist)
203
204
205 --| Execution
206 curl_easy.set_read_function (curl_handle)
207 curl_easy.set_write_function (curl_handle)
208 if is_debug then
209 curl_easy.set_debug_function (curl_handle)
210 curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_verbose, 1)
211 end
212
213 --| Write options
214
215 if ctx /= Void and then ctx.has_write_option then
216 if l_custom_function = Void then
217 create l_custom_function.make
218 end
219 if attached ctx.write_agent as l_write_agent then
220 l_custom_function.set_write_procedure (l_write_agent)
221 elseif attached ctx.output_content_file as l_output_content_file then
222 create l_curl_string.make_empty
223 l_custom_function.set_write_procedure (new_write_content_data_to_file_agent (l_output_content_file, l_curl_string))
224 -- l_curl_string will contain the raw header, used to fill `Result'
225 elseif attached ctx.output_file as l_output_file then
226 create l_curl_string.make_empty
227 l_custom_function.set_write_procedure (new_write_data_to_file_agent (l_output_file, l_curl_string))
228 -- l_curl_string will contain the raw header, used to fill `Result'
229 end
230 curl_easy.set_curl_function (l_custom_function)
231 else
232 create l_curl_string.make_empty
233 curl_easy.setopt_curl_string (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_writedata, l_curl_string)
234 end
235
236 create Result.make (l_url)
237 l_result := curl_easy.perform (curl_handle)
238
239 --| Result
240 if l_result = {CURL_CODES}.curle_ok then
241 Result.status := response_status_code (curl_easy, curl_handle)
242 if l_curl_string /= Void then
243 Result.set_response_message (l_curl_string.string, ctx)
244 end
245 else
246 Result.set_error_message ("Error: cURL Error[" + l_result.out + "]")
247 Result.status := response_status_code (curl_easy, curl_handle)
248 end
249
250 --| Cleaning
251
252 curl.global_cleanup
253 curl_easy.cleanup (curl_handle)
254 else
255 create Result.make (url)
256 Result.set_error_message ("Error: internal error")
257 end
258
259 --| Remaining cleaning
260 if l_form /= Void then
261 l_form.dispose
262 end
263 if curl /= Void and then p_slist /= default_pointer then
264 curl.slist_free_all (p_slist)
265 end
266 if l_upload_file /= Void and then not l_upload_file.is_closed then
267 l_upload_file.close
268 end
269 rescue
270 retried := True
271 if curl /= Void then
272 curl.global_cleanup
273 curl := Void
274 end
275 if curl_easy /= Void and curl_handle /= default_pointer then
276 curl_easy.cleanup (curl_handle)
277 curl_easy := Void
278 end
279 retry
280 end
281
282 initialize_curl_session (ctx: like context; curl: CURL_EXTERNALS; curl_easy: CURL_EASY_EXTERNALS; curl_handle: POINTER)
283 local
284 l_proxy: like proxy
285 do
286 --| RESPONSE HEADERS
287 curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_header, 1)
288
289 --| PROXY ...
290
291 if ctx /= Void then
292 l_proxy := ctx.proxy
293 end
294 if l_proxy = Void then
295 l_proxy := proxy
296 end
297 if l_proxy /= Void then
298 curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_proxyport, l_proxy.port)
299 curl_easy.setopt_string (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_proxy, l_proxy.host)
300 end
301
302 --| Timeout
303 if timeout > 0 then
304 curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_timeout, timeout)
305 end
306 --| Connect Timeout
307 if connect_timeout > 0 then
308 curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_connecttimeout, timeout)
309 end
310 --| Redirection
311 if max_redirects /= 0 then
312 curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_followlocation, 1)
313 curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_maxredirs, max_redirects)
314 else
315 curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_followlocation, 0)
316 end
317
318 --| SSL
319 if is_insecure then
320 curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_ssl_verifyhost, 0)
321 curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_ssl_verifypeer, 0)
322 end
323
324 --| Request method
325 if request_method.is_case_insensitive_equal ("GET") then
326 curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_httpget, 1)
327 elseif request_method.is_case_insensitive_equal ("POST") then
328 curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_post, 1)
329 elseif request_method.is_case_insensitive_equal ("PUT") then
330 curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_put, 1)
331 elseif request_method.is_case_insensitive_equal ("HEAD") then
332 curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_nobody, 1)
333 elseif request_method.is_case_insensitive_equal ("DELETE") then
334 curl_easy.setopt_string (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_customrequest, "DELETE")
335 else
336 curl_easy.setopt_string (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_customrequest, request_method)
337 --| ignored
338 end
339 end
340
341 feature {NONE} -- Implementation
342
343 response_status_code (curl_easy: CURL_EASY_EXTERNALS; curl_handle: POINTER): INTEGER
344 local
345 l_result: INTEGER
346 a_data: CELL [detachable ANY]
347 do
348 create a_data.put (Void)
349 l_result := curl_easy.getinfo (curl_handle, {CURL_INFO_CONSTANTS}.curlinfo_response_code, a_data)
350 if l_result = 0 and then attached {INTEGER} a_data.item as l_http_status then
351 Result := l_http_status
352 else
353 Result := 0
354 end
355 end
356
357 new_write_data_to_file_agent (f: FILE; h: detachable STRING): PROCEDURE [ANY, TUPLE [READABLE_STRING_8]]
358 -- Write all downloaded header and content data into `f'
359 -- and write raw header into `h' if attached.
360 do
361 Result := agent (s: READABLE_STRING_8; ia_header: detachable STRING; ia_file: FILE; ia_header_fetched: CELL [BOOLEAN])
362 do
363 ia_file.put_string (s)
364 if ia_header /= Void and not ia_header_fetched.item then
365 ia_header.append (s)
366 if s.starts_with ("%R%N") then
367 ia_header_fetched.replace (True)
368 end
369 end
370 end (?, h, f, create {CELL [BOOLEAN]}.put (False))
371 end
372
373 new_write_content_data_to_file_agent (f: FILE; h: STRING): PROCEDURE [ANY, TUPLE [READABLE_STRING_8]]
374 -- Write all downloaded content data into `f' (without raw header)
375 -- and write raw header into `h' if attached.
376 do
377 Result := agent (s: READABLE_STRING_8; ia_header: detachable STRING; ia_file: FILE; ia_header_fetched: CELL [BOOLEAN])
378 do
379 if ia_header_fetched.item then
380 ia_file.put_string (s)
381 else
382 if ia_header /= Void then
383 ia_header.append (s)
384 end
385 if s.starts_with ("%R%N") then
386 ia_header_fetched.replace (True)
387 end
388 end
389 end (?, h, f, create {CELL [BOOLEAN]}.put (False))
390 end
391
392 note
393 copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
394 license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
395 source: "[
396 Eiffel Software
397 5949 Hollister Ave., Goleta, CA 93117 USA
398 Telephone 805-685-1006, Fax 805-685-6869
399 Website http://www.eiffel.com
400 Customer support http://support.eiffel.com
401 ]"
402 end

Properties

Name Value
svn:eol-style native
svn:keywords Author Date ID Revision

  ViewVC Help
Powered by ViewVC 1.1.23