lib/ios: implement `app::http_request`
[nit.git] / lib / app / http_request.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
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
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
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.
14
15 # HTTP request services: `AsyncHttpRequest` and `Text::http_get`
16 module http_request
17
18 import app_base
19 import pthreads
20 import json::serialization
21
22 import linux::http_request is conditional(linux)
23 import android::http_request is conditional(android)
24 import ios::http_request is conditional(ios)
25
26 redef class App
27 # Platform specific service to execute `task` on the main/UI thread
28 fun run_on_ui_thread(task: Task) is abstract
29 end
30
31 # Thread executing an HTTP request and deserializing JSON asynchronously
32 #
33 # This class defines four methods acting on the main/UI thread,
34 # they should be implemented as needed:
35 # * before
36 # * on_load
37 # * on_fail
38 # * after
39 class AsyncHttpRequest
40 super Thread
41
42 # Root URI of the remote server
43 fun rest_server_uri: String is abstract
44
45 # Action, or path, for this request within the `rest_server_uri`
46 fun rest_action: String is abstract
47
48 # Should the response content be deserialized from JSON?
49 var deserialize_json = true is writable
50
51 redef fun start
52 do
53 before
54 super
55 end
56
57 redef fun main
58 do
59 var uri = rest_server_uri / rest_action
60
61 # Execute REST request
62 var rep = uri.http_get
63 if rep.is_error then
64 app.run_on_ui_thread new RestRunnableOnFail(self, rep.error)
65 return null
66 end
67
68 if not deserialize_json then
69 app.run_on_ui_thread new RestRunnableOnLoad(self, rep)
70 return null
71 end
72
73 # Deserialize
74 var deserializer = new JsonDeserializer(rep.value)
75 var res = deserializer.deserialize
76 if deserializer.errors.not_empty then
77 app.run_on_ui_thread new RestRunnableOnFail(self, deserializer.errors.first)
78 end
79
80 app.run_on_ui_thread new RestRunnableOnLoad(self, res)
81 return null
82 end
83
84 # Prepare the UI or other parts of the program before executing the REST request
85 fun before do end
86
87 # Invoked when the HTTP request returned valid data
88 #
89 # If `deserialize_json`, the default behavior, this method is invoked only if deserialization was successful.
90 # In this case, `result` may be any deserialized object.
91 #
92 # Otherwise, if `not deserialize_json`, `result` contains the content of the response as a `String`.
93 fun on_load(result: nullable Object) do end
94
95 # Invoked when the HTTP request has failed and no data was received or deserialization failed
96 fun on_fail(error: Error) do print_error "REST request '{rest_action}' failed with: {error}"
97
98 # Complete this request whether it was a success or not
99 fun after do end
100 end
101
102 redef class Text
103 # Execute an HTTP GET request synchronously at the URI `self`
104 #
105 # ~~~nitish
106 # var response = "http://example.org/".http_get
107 # if response.is_error then
108 # print_error response.error
109 # else
110 # print "HTTP status code: {response.code}"
111 # print response.value
112 # end
113 # ~~~
114 private fun http_get: HttpRequestResult is abstract
115 end
116
117 # Result of a call to `Text::http_get`
118 #
119 # Users should first check if `is_error` to use `error`.
120 # Otherwise they can use `value` to get the content of the response
121 # and `code` for the HTTP status code.
122 class HttpRequestResult
123 super MaybeError[String, Error]
124
125 # The HTTP status code, if any
126 var maybe_code: nullable Int
127
128 # The status code
129 # Require: `not is_error`
130 fun code: Int do return maybe_code.as(not null)
131 end
132
133 private abstract class HttpRequestTask
134 super Task
135
136 # `AsyncHttpRequest` to which send callbacks
137 var sender_thread: AsyncHttpRequest
138 end
139
140 private class RestRunnableOnLoad
141 super HttpRequestTask
142
143 var res: nullable Object
144
145 redef fun main
146 do
147 sender_thread.on_load(res)
148 sender_thread.after
149 end
150 end
151
152 private class RestRunnableOnFail
153 super HttpRequestTask
154
155 var error: Error
156
157 redef fun main
158 do
159 sender_thread.on_fail(error)
160 sender_thread.after
161 end
162 end