HTTP request services: AsyncHttpRequest and Text::http_get

import app::http_request

class MyHttpRequest
    super AsyncHttpRequest

    redef fun uri do return "http://example.com/"

    redef fun on_load(data, status) do print "Received: {data or else "null"}"

    redef fun on_fail(error) do print "Connection error: {error}"
end

var req = new MyHttpRequest
req.start

Introduced classes

abstract class AsyncHttpRequest

app :: AsyncHttpRequest

Thread executing an HTTP request asynchronously
class HttpRequestResult

app :: HttpRequestResult

Result of a call to Text::http_get
class SimpleAsyncHttpRequest

app :: SimpleAsyncHttpRequest

Simple AsyncHttpRequest where uri is an attribute

Redefined classes

redef class App

app :: http_request $ App

App subclasses are cross-platform applications
redef abstract class Text

app :: http_request $ Text

High-level abstraction for all text representations

All class definitions

redef class App

app :: http_request $ App

App subclasses are cross-platform applications
abstract class AsyncHttpRequest

app $ AsyncHttpRequest

Thread executing an HTTP request asynchronously
class HttpRequestResult

app $ HttpRequestResult

Result of a call to Text::http_get
class SimpleAsyncHttpRequest

app $ SimpleAsyncHttpRequest

Simple AsyncHttpRequest where uri is an attribute
redef abstract class Text

app :: http_request $ Text

High-level abstraction for all text representations
package_diagram app::http_request http_request app::app_base app_base app::http_request->app::app_base pthreads pthreads app::http_request->pthreads json json app::http_request->json core core app::app_base->core pthreads->core parser_base parser_base json->parser_base serialization serialization json->serialization ...core ... ...core->core ...parser_base ... ...parser_base->parser_base ...serialization ... ...serialization->serialization android::http_request http_request android::http_request->app::http_request app::http_request_example http_request_example app::http_request_example->app::http_request ios::http_request http_request ios::http_request->app::http_request linux::http_request http_request linux::http_request->app::http_request a_star-m a_star-m a_star-m->android::http_request a_star-m->app::http_request_example a_star-m->ios::http_request a_star-m->linux::http_request a_star-m... ... a_star-m...->a_star-m

Ancestors

module abstract_collection

core :: abstract_collection

Abstract collection classes and services.
module abstract_text

core :: abstract_text

Abstract class for manipulation of sequences of characters
module array

core :: array

This module introduces the standard array structure.
module bitset

core :: bitset

Services to handle BitSet
module bytes

core :: bytes

Services for byte streams and arrays
module caching

serialization :: caching

Services for caching serialization engines
module circular_array

core :: circular_array

Efficient data structure to access both end of the sequence.
module codec_base

core :: codec_base

Base for codecs to use with streams
module codecs

core :: codecs

Group module for all codec-related manipulations
module collection

core :: collection

This module define several collection classes.
module core

core :: core

Standard classes and methods used by default by Nit programs and libraries.
module engine_tools

serialization :: engine_tools

Advanced services for serialization engines
module environ

core :: environ

Access to the environment variables of the process
module error

core :: error

Standard error-management infrastructure.
module error

json :: error

Intro JsonParseError which is exposed by all JSON reading APIs
module exec

core :: exec

Invocation and management of operating system sub-processes.
module file

core :: file

File manipulations (create, read, write, etc.)
module fixed_ints

core :: fixed_ints

Basic integers of fixed-precision
module fixed_ints_text

core :: fixed_ints_text

Text services to complement fixed_ints
module flat

core :: flat

All the array-based text representations
module gc

core :: gc

Access to the Nit internal garbage collection mechanism
module hash_collection

core :: hash_collection

Introduce HashMap and HashSet.
module inspect

serialization :: inspect

Refine Serializable::inspect to show more useful information
module iso8859_1

core :: iso8859_1

Codec for ISO8859-1 I/O
module kernel

core :: kernel

Most basic classes and methods.
module list

core :: list

This module handle double linked lists
module math

core :: math

Mathematical operations
module meta

meta :: meta

Simple user-defined meta-level to manipulate types of instances as object.
module native

core :: native

Native structures for text and bytes
module numeric

core :: numeric

Advanced services for Numeric types
module parser_base

parser_base :: parser_base

Simple base for hand-made parsers of all kinds
module poset

poset :: poset

Pre order sets and partial order set (ie hierarchies)
module protocol

core :: protocol

module queue

core :: queue

Queuing data structures and wrappers
module range

core :: range

Module for range of discrete objects.
module re

core :: re

Regular expression support for all services based on Pattern
module ropes

core :: ropes

Tree-based representation of a String.
module safe

serialization :: safe

Services for safer deserialization engines
module serialization

serialization :: serialization

General serialization services
module serialization_core

serialization :: serialization_core

Abstract services to serialize Nit objects to different formats
module serialization_read

json :: serialization_read

Services to read JSON: deserialize_json and JsonDeserializer
module serialization_write

json :: serialization_write

Services to write Nit objects to JSON strings: serialize_to_json and JsonSerializer
module sorter

core :: sorter

This module contains classes used to compare things and sorts arrays.
module static

json :: static

Static interface to read Nit objects from JSON strings
module stream

core :: stream

Input and output streams of characters
module text

core :: text

All the classes and methods related to the manipulation of text entities
module time

core :: time

Management of time and dates
module union_find

core :: union_find

union–find algorithm using an efficient disjoint-set data structure
module utf8

core :: utf8

Codec for UTF-8 I/O

Parents

module app_base

app :: app_base

Base of the app.nit framework, defines App
module json

json :: json

Read and write JSON formatted text using the standard serialization services
module pthreads

pthreads :: pthreads

Main POSIX threads support and intro the classes Thread, Mutex and Barrier

Children

module http_request

android :: http_request

Android implementation of app:http_request
module http_request

ios :: http_request

Implementation of app::http_request for iOS
module http_request

linux :: http_request

Implementation of app::http_request using GDK and Curl
module http_request_example

app :: http_request_example

Example for the app::http_request main service AsyncHttpRequest

Descendants

module a_star-m

a_star-m

# HTTP request services: `AsyncHttpRequest` and `Text::http_get`
#
# ~~~nitish
# import app::http_request
#
# class MyHttpRequest
#     super AsyncHttpRequest
#
#     redef fun uri do return "http://example.com/"
#
#     redef fun on_load(data, status) do print "Received: {data or else "null"}"
#
#     redef fun on_fail(error) do print "Connection error: {error}"
# end
#
# var req = new MyHttpRequest
# req.start
# ~~~
module http_request

import app_base
import pthreads
import json

import linux::http_request is conditional(linux)
import android::http_request is conditional(android)
import ios::http_request is conditional(ios)

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 asynchronously
#
# The request is sent to `uri`.
# Either `uri`, or `uri_root` and `uri_tail`, must be set in subclasses.
#
# If `deserialize_json`, the default behavior, the response is deserialized from JSON
#
# If `delay > 0.0`, sending the request is delayed by the given `delay` in seconds.
# It can be used to delay resending a request on error.
#
# Four callback methods act on the main/UI thread,
# they should be implemented as needed in subclasses:
# * `before`
# * `on_load`
# * `on_fail`
# * `after`
#
# See full example at `examples/http_request_example.nit`.
abstract class AsyncHttpRequest
	super Thread

	# URI target of this request, by default it is composed of `uri_root / uri_tail`
	fun uri: Text do return uri_root / uri_tail

	# Root URI of the remote server, usually the scheme and remote host
	fun uri_root: String is abstract

	# Right part of the URI, after `uri_root`, often the resource path and the query
	fun uri_tail: String do return ""

	# Should the response content be deserialized from JSON?
	var deserialize_json = true is writable

	# Delay in seconds before sending this request
	var delay = 0.0 is writable

	redef fun start
	do
		before
		super
	end

	redef fun main
	do
		var delay = delay
		if delay > 0.0 then delay.sleep

		var uri = uri

		# 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 deserialize_json then
			# 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)
			else
				app.run_on_ui_thread new RestRunnableOnLoad(self, res, rep.code)
			end
		else
			# Return text data
			app.run_on_ui_thread new RestRunnableOnLoad(self, rep.value, rep.code)
			return null
		end

		app.run_on_ui_thread new RestRunnableJoin(self)

		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, http_status_code: Int) 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 "HTTP request '{uri}' failed with: {error}"

	# Complete this request whether it was a success or not
	fun after do end
end

# Simple `AsyncHttpRequest` where `uri` is an attribute
#
# Prints on communication errors and when the remote server returns an
# HTTP status code not in the 200s.
#
# This class can be instantiated to execute a request where the response is
# ignored by the application. Alternatively, it can be subclassed to implement
# `on_load`.
#
# ~~~nitish
# var request = new SimpleAsyncHttpRequest("http://example.com")
# request.start
# ~~~
class SimpleAsyncHttpRequest
	super AsyncHttpRequest

	redef var uri

	redef fun on_load(data, status) do if status < 200 or status >= 299
	then print_error "HTTP request '{uri}' received HTTP status code: {status}"
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

	var code: Int

	redef fun main
	do
		sender_thread.on_load(res, code)
		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

private class RestRunnableJoin
	super HttpRequestTask

	redef fun main do sender_thread.join
end
lib/app/http_request.nit:15,1--233,3