From: Alexis Laferrière Date: Sat, 31 Oct 2015 23:54:57 +0000 (-0400) Subject: lib/app: intro the http_request API X-Git-Tag: v0.8~97^2~4 X-Git-Url: http://nitlanguage.org?hp=cd071f50c0f6e0999dd6e566d80a9534f4bb9f5d lib/app: intro the http_request API Signed-off-by: Alexis Laferrière --- diff --git a/lib/app/http_request.nit b/lib/app/http_request.nit new file mode 100644 index 0000000..70990a9 --- /dev/null +++ b/lib/app/http_request.nit @@ -0,0 +1,161 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# HTTP request services: `AsyncHttpRequest` and `Text::http_get` +module http_request + +import app_base +import pthreads +import json::serialization + +import linux::http_request is conditional(linux) +import android::http_request is conditional(android) + +redef class App + # Platform specific service to execute `task` on the main/UI thread + fun run_on_ui_thread(task: Task) is abstract +end + +# Thread executing an HTTP request and deserializing JSON asynchronously +# +# This class defines four methods acting on the main/UI thread, +# they should be implemented as needed: +# * before +# * on_load +# * on_fail +# * after +class AsyncHttpRequest + super Thread + + # Root URI of the remote server + fun rest_server_uri: String is abstract + + # Action, or path, for this request within the `rest_server_uri` + fun rest_action: String is abstract + + # Should the response content be deserialized from JSON? + var deserialize_json = true is writable + + redef fun start + do + before + super + end + + redef fun main + do + var uri = rest_server_uri / rest_action + + # Execute REST request + var rep = uri.http_get + if rep.is_error then + app.run_on_ui_thread new RestRunnableOnFail(self, rep.error) + return null + end + + if not deserialize_json then + app.run_on_ui_thread new RestRunnableOnLoad(self, rep) + return null + end + + # Deserialize + var deserializer = new JsonDeserializer(rep.value) + var res = deserializer.deserialize + if deserializer.errors.not_empty then + app.run_on_ui_thread new RestRunnableOnFail(self, deserializer.errors.first) + end + + app.run_on_ui_thread new RestRunnableOnLoad(self, res) + return null + end + + # Prepare the UI or other parts of the program before executing the REST request + fun before do end + + # Invoked when the HTTP request returned valid data + # + # If `deserialize_json`, the default behavior, this method is invoked only if deserialization was successful. + # In this case, `result` may be any deserialized object. + # + # Otherwise, if `not deserialize_json`, `result` contains the content of the response as a `String`. + fun on_load(result: nullable Object) do end + + # Invoked when the HTTP request has failed and no data was received or deserialization failed + fun on_fail(error: Error) do print_error "REST request '{rest_action}' failed with: {error}" + + # Complete this request whether it was a success or not + fun after do end +end + +redef class Text + # Execute an HTTP GET request synchronously at the URI `self` + # + # ~~~nitish + # var response = "http://example.org/".http_get + # if response.is_error then + # print_error response.error + # else + # print "HTTP status code: {response.code}" + # print response.value + # end + # ~~~ + private fun http_get: HttpRequestResult is abstract +end + +# Result of a call to `Text::http_get` +# +# Users should first check if `is_error` to use `error`. +# Otherwise they can use `value` to get the content of the response +# and `code` for the HTTP status code. +class HttpRequestResult + super MaybeError[String, Error] + + # The HTTP status code, if any + var maybe_code: nullable Int + + # The status code + # Require: `not is_error` + fun code: Int do return maybe_code.as(not null) +end + +private abstract class HttpRequestTask + super Task + + # `AsyncHttpRequest` to which send callbacks + var sender_thread: AsyncHttpRequest +end + +private class RestRunnableOnLoad + super HttpRequestTask + + var res: nullable Object + + redef fun main + do + sender_thread.on_load(res) + sender_thread.after + end +end + +private class RestRunnableOnFail + super HttpRequestTask + + var error: Error + + redef fun main + do + sender_thread.on_fail(error) + sender_thread.after + end +end