1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # HTTP request services: `AsyncHttpRequest` and `Text::http_get`
20 import json
::serialization
22 import linux
::http_request
is conditional
(linux
)
23 import android
::http_request
is conditional
(android
)
24 import ios
::http_request
is conditional
(ios
)
27 # Platform specific service to execute `task` on the main/UI thread
28 fun run_on_ui_thread
(task
: Task) is abstract
31 # Thread executing an HTTP request asynchronously
33 # The request is sent to `uri`.
34 # Either `uri`, or `uri_root` and `uri_tail`, must be set in subclasses.
36 # If `deserialize_json`, the default behavior, the response is deserialized from JSON
38 # If `delay > 0.0`, sending the request is delayed by the given `delay` in seconds.
39 # It can be used to delay resending a request on error.
41 # Four callback methods act on the main/UI thread,
42 # they should be implemented as needed in subclasses:
48 # See full example at `examples/http_request_example.nit`.
49 abstract class AsyncHttpRequest
52 # URI target of this request, by default it is composed of `uri_root / uri_tail`
53 fun uri
: Text do return uri_root
/ uri_tail
55 # Root URI of the remote server, usually the scheme and remote host
56 fun uri_root
: String is abstract
58 # Right part of the URI, after `uri_root`, often the resource path and the query
59 fun uri_tail
: String do return ""
61 # Should the response content be deserialized from JSON?
62 var deserialize_json
= true is writable
64 # Delay in seconds before sending this request
65 var delay
= 0.0 is writable
76 if delay
> 0.0 then delay
.sleep
80 # Execute REST request
81 var rep
= uri
.http_get
83 app
.run_on_ui_thread
new RestRunnableOnFail(self, rep
.error
)
87 if deserialize_json
then
89 var deserializer
= new JsonDeserializer(rep
.value
)
90 var res
= deserializer
.deserialize
91 if deserializer
.errors
.not_empty
then
92 app
.run_on_ui_thread
new RestRunnableOnFail(self, deserializer
.errors
.first
)
94 app
.run_on_ui_thread
new RestRunnableOnLoad(self, res
, rep
.code
)
98 app
.run_on_ui_thread
new RestRunnableOnLoad(self, rep
.value
, rep
.code
)
102 app
.run_on_ui_thread
new RestRunnableJoin(self)
107 # Prepare the UI or other parts of the program before executing the REST request
110 # Invoked when the HTTP request returned valid data
112 # If `deserialize_json`, the default behavior, this method is invoked only if deserialization was successful.
113 # In this case, `result` may be any deserialized object.
115 # Otherwise, if `not deserialize_json`, `result` contains the content of the response as a `String`.
116 fun on_load
(result
: nullable Object, http_status_code
: Int) do end
118 # Invoked when the HTTP request has failed and no data was received or deserialization failed
119 fun on_fail
(error
: Error) do print_error
"HTTP request '{uri}' failed with: {error}"
121 # Complete this request whether it was a success or not
125 # Minimal implementation of `AsyncHttpRequest` where `uri` is an attribute
127 # Prints on communication errors and when the server returns an HTTP status code not in the 200s.
130 # var request = new SimpleAsyncHttpRequest("http://example.com")
133 class SimpleAsyncHttpRequest
134 super AsyncHttpRequest
138 redef fun on_load
(data
, status
) do if status
< 200 or status
>= 299
139 then print_error
"HTTP request '{uri}' received HTTP status code: {status}"
143 # Execute an HTTP GET request synchronously at the URI `self`
146 # var response = "http://example.org/".http_get
147 # if response.is_error then
148 # print_error response.error
150 # print "HTTP status code: {response.code}"
151 # print response.value
154 private fun http_get
: HttpRequestResult is abstract
157 # Result of a call to `Text::http_get`
159 # Users should first check if `is_error` to use `error`.
160 # Otherwise they can use `value` to get the content of the response
161 # and `code` for the HTTP status code.
162 class HttpRequestResult
163 super MaybeError[String, Error]
165 # The HTTP status code, if any
166 var maybe_code
: nullable Int
170 # Require: `not is_error`
171 fun code
: Int do return maybe_code
.as(not null)
174 private abstract class HttpRequestTask
177 # `AsyncHttpRequest` to which send callbacks
178 var sender_thread
: AsyncHttpRequest
181 private class RestRunnableOnLoad
182 super HttpRequestTask
184 var res
: nullable Object
190 sender_thread
.on_load
(res
, code
)
195 private class RestRunnableOnFail
196 super HttpRequestTask
202 sender_thread
.on_fail
(error
)
207 private class RestRunnableJoin
208 super HttpRequestTask
210 redef fun main
do sender_thread
.join