Skip to content
Newer
Older
100644 223 lines (196 sloc) 6.53 KB
509973a @BobDickinson Added async IO support
authored Feb 23, 2012
1 --===========================================================================--
2 -- Module: async_http
3 --===========================================================================--
4 --
5 -- Solution originally found at:
6 --
7 -- http://developer.anscamobile.com/code/cross-platform-asynchronous-http-request
8 --
9 -- Header and license from original file are included below, per the license.
10 -- Note that this module has been modified significantly from the original
11 -- implementation.
12 --
13 --[[
14 File: asyncHttp.lua
15 Purpose: Asynchronous HTTP request for Corona SDK
16 Author: Le Viet Bach
17 Company: Rubycell .JSC
18
19
20 Copyright (c) 2011 Le Viet Bach
21
22 This software is provided 'as-is', without any express or implied
23 warranty. In no event will the authors be held liable for any damages
24 arising from the use of this software.
25
26 Permission is granted to anyone to use this software for any purpose,
27 including commercial applications, and to alter it and redistribute it
28 freely, subject to the following restrictions:
29
30 1. The origin of this software must not be misrepresented; you must not
31 claim that you wrote the original software. If you use this software
32 in a product, an acknowledgment in the product documentation would be
33 appreciated but is not required.
34
35 2. Altered source versions must be plainly marked as such, and must not be
36 misrepresented as being the original software.
37
38 3. This notice may not be removed or altered from any source
39 distribution.
40 ]]--
41 -------------------------------------------------------------------------------
42 --
43 -- Note: Make sure to include (require) this module before any other modules that use
44 -- any Lua sockets functionality.
45 --
46 -- Usage:
47 --
48 -- async_http.request( httpRequest [,listener] )
49 --
50 -- Where httpRequest is a table containing the following
51 -- url - the url
52 -- method - the HTTP method, such as GET or POST
53 -- headers (optional) - request headers
54 -- body (optional) - request body
55 -- proxy (optional) - proxy to be used, in the form "http://hostAddress:hostPort"
56 --
57 -- This function produces an http request "status".
58 --
59 -- If no listener is provided, then this function blocks while completing the request
60 -- and returns the status to the caller.
61 --
62 -- If a listener is provided, then this function will perform the request asynchronously,
63 -- returning a cancellable thread reference. When the request is completed, the
64 -- listener will be called with the status.
65 --
66 -- status
67 -- isError - true or false
68 -- errorMessage - error message, if isError is true
69 -- request - the originally provided httpRequest
70 -- response - if isError is false, the http response table
71 -- code - response code, such as 200 for success
72 -- status - resonse status string, such as "OK" or "Not Found"
73 -- headers - response headers
74 -- body - response body (if any)
75 --
76 -- Example:
77 --
78 -- local httpRequest = {
79 -- url = "http://www.google.com",
80 -- method = "GET",
81 -- headers = { Date = os.date("!%a, %d %b %Y %H:%M:%S GMT") },
82 -- }
83 --
84 -- -- Blocking get
85 -- status = async_http.request(httpRequest)
86 -- if not status.isError and status.response.code == 200 then
87 -- print("Blocking get - page length was: ", status.response.body:len())
88 -- end
89 --
90 -- -- Asynchronous get
91 -- local function onGetComplete( status )
92 -- if not status.isError and status.response.code == 200 then
93 -- print("Asynchronous get - page length was: ", status.response.body:len())
94 -- end
95 -- end
96 -- http_thread = async_http.request(httpRequest, onGetComplete)
97 --
98 -- -- Call http_thread:cancel() to terminate before completion, if desired
99 --
100 ------------------------------------------------------------------------
101 --
102
103 local M = {}
104
105 local socket = require "socket"
106 local dispatch = require "dispatch"
107 local http = require "socket.http"
108 local ltn12 = require "ltn12"
109 dispatch.TIMEOUT = 10
110 local Runtime = Runtime
111 local table = table
112 local print = print
113 local coroutine = coroutine
114
115 local function blockingRequest( httpRequest )
116
117 local body
118 if httpRequest.body then
119 body = ltn12.source.string(httpRequest.body)
120 end
121
122 local httpResponse = {
123 body = {},
124 }
125
126 local result
127 result, httpResponse.code, httpResponse.headers, httpResponse.status = http.request{
128 url = httpRequest.url,
129 method = httpRequest.method,
130 headers = httpRequest.headers,
131 source = body,
132 sink = ltn12.sink.table(httpResponse.body),
133 proxy = httpRequest.proxy,
134 }
135 if result then
136 httpResponse.body = table.concat(httpResponse.body)
137 return {
138 isError = false,
139 request = httpRequest,
140 response = httpResponse,
141 }
142 else
143 return {
144 isError = true,
145 errorMessage = httpResponse.code,
146 request = httpRequest,
147 }
148 end
149
150 end
151
152 local function asyncRequest( httpRequest, listener )
153
154 local handler = dispatch.newhandler("coroutine")
155 local running = true
156
157 handler:start(function()
158
159 local body
160 if httpRequest.body then
161 body = ltn12.source.string(httpRequest.body)
162 end
163
164 local httpResponse = {
165 body = {},
166 }
167
168 local result
169 result, httpResponse.code, httpResponse.headers, httpResponse.status = http.request{
170 url = httpRequest.url,
171 method = httpRequest.method,
172 create = handler.tcp,
173 headers = httpRequest.headers,
174 source = body,
175 sink = ltn12.sink.table(httpResponse.body),
176 proxy = httpRequest.proxy,
177 }
178 if result then
179 httpResponse.body = table.concat(httpResponse.body)
180 listener{
181 isError = false,
182 request = httpRequest,
183 response = httpResponse,
184 }
185 else
186 listener{
187 isError = true,
188 errorMessage = httpResponse.code,
189 request = httpRequest,
190 }
191 end
192 running = false
193
194 end)
195
196 local httpThread = {}
197
198 function httpThread.enterFrame()
199 if running then
200 handler:step()
201 else
202 Runtime:removeEventListener("enterFrame", httpThread)
203 end
204 end
205
206 function httpThread:cancel()
207 Runtime:removeEventListener("enterFrame", self)
208 handler = nil
209 end
210
211 Runtime:addEventListener("enterFrame", httpThread)
212 return httpThread
213 end
214
215 function M.request( httpRequest, listener )
216 if listener then
217 return asyncRequest(httpRequest, listener)
218 else
219 return blockingRequest(httpRequest)
220 end
221 end
222
223 return M
Something went wrong with that request. Please try again.