public
Description: A cross-platform web server that's scripted with Nu.
Homepage: http://programming.nu
Clone URL: git://github.com/timburks/nunja.git
Preliminary support for asynchronous dns and http queries.

These additions are to allow Nunja-based web applications
to call out to other servers asynchronously. Requests
made using the internal API are accompanied by blocks
that specify actions to be performed when queries
complete.

All new interfaces are preliminary and are likely
to change. See sample/site.nu for usage.
timburks (author)
Thu May 08 14:06:18 -0700 2008
commit  f635b744dd371076b926a6cc1dae1b00158bca22
tree    a45b3a2467387ccf805d37c80ca3e7dfd131b1f6
parent  592c6fecd13d44e60e37494bfe228f299e7c02f8
...
161
162
163
 
 
 
 
164
165
166
...
193
194
195
196
 
197
198
199
...
229
230
231
232
 
233
234
235
236
 
237
238
239
...
242
243
244
245
 
246
247
248
249
 
250
251
252
253
254
 
255
...
161
162
163
164
165
166
167
168
169
170
...
197
198
199
 
200
201
202
203
...
233
234
235
 
236
237
238
239
 
240
241
242
243
...
246
247
248
 
249
250
251
252
 
253
254
255
256
257
 
258
259
0
@@ -161,6 +161,10 @@
0
             (then (set BODY (eval @statements))) ;; deprecated, evaluates statements in the instance method context
0
             (else (set BODY (@statements @match request response)))) ;; new style, evaluates a function with a lexical closure
0
         
0
+ (unless BODY
0
+ (puts "returning early, leaving connection open")
0
+ (return))
0
+
0
         (if (BODY isKindOfClass:NSString)
0
             (then (set html "<head>\n")
0
                   (if (response "HEAD")
0
@@ -193,7 +197,7 @@
0
      (- (id) initWithSite:(id) site is
0
         (self init)
0
         (set @handlers (array))
0
- (set $nunja self)
0
+ (set $nunjaDelegate self)
0
         (set @root site)
0
         (load (+ site "/site.nu"))
0
         self)
0
@@ -229,11 +233,11 @@
0
                  (then
0
                       (set __pattern (eval (car margs)))
0
                       (set __statements (cdr margs))
0
- (($nunja handlers) << (NunjaRequestHandler handlerWithAction:"GET" pattern:__pattern statements:__statements)))
0
+ (($nunjaDelegate handlers) << (NunjaRequestHandler handlerWithAction:"GET" pattern:__pattern statements:__statements)))
0
                  (else
0
                       (set __pattern (eval (car margs)))
0
                       (set __function (eval (append '(do (MATCH REQUEST RESPONSE) (nunja-site-prefix)) (cdr margs))))
0
- (($nunja handlers) << (NunjaRequestHandler handlerWithAction:"GET" pattern:__pattern statements:__function))))))
0
+ (($nunjaDelegate handlers) << (NunjaRequestHandler handlerWithAction:"GET" pattern:__pattern statements:__function))))))
0
 
0
 ;; Declare a post action.
0
 (global post
0
@@ -242,14 +246,14 @@
0
                  (then
0
                       (set __pattern (eval (car margs)))
0
                       (set __statements (cdr margs))
0
- (($nunja handlers) << (NunjaRequestHandler handlerWithAction:"POST" pattern:__pattern statements:__statements)))
0
+ (($nunjaDelegate handlers) << (NunjaRequestHandler handlerWithAction:"POST" pattern:__pattern statements:__statements)))
0
                  (else
0
                       (set __pattern (eval (car margs)))
0
                       (set __function (eval (append '(do (MATCH REQUEST RESPONSE) (nunja-site-prefix)) (cdr margs))))
0
- (($nunja handlers) << (NunjaRequestHandler handlerWithAction:"POST" pattern:__pattern statements:__function))))))
0
+ (($nunjaDelegate handlers) << (NunjaRequestHandler handlerWithAction:"POST" pattern:__pattern statements:__function))))))
0
 
0
 ;; Set the top-level directory for a site
0
 (global root
0
         (macro _
0
- ($nunja setRoot:(eval (car margs)))))
0
+ ($nunjaDelegate setRoot:(eval (car margs)))))
0
 
0
...
51
52
53
 
54
55
56
...
51
52
53
54
55
56
57
0
@@ -51,6 +51,7 @@
0
        (set argi (+ argi 1)))
0
 
0
 (set n ((Nunja alloc) init))
0
+(set $nunja n)
0
 (n bindToAddress:"0.0.0.0" port:port)
0
 (if site
0
     (n setDelegate:((NunjaDelegate alloc) initWithSite:site)))
...
22
23
24
 
 
 
25
26
27
...
160
161
162
 
 
163
164
165
 
166
167
168
...
204
205
206
 
207
208
209
...
238
239
240
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
241
...
22
23
24
25
26
27
28
29
30
...
163
164
165
166
167
168
169
 
170
171
172
173
...
209
210
211
212
213
214
215
...
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
0
@@ -22,6 +22,9 @@ limitations under the License.
0
 #include <event.h>
0
 #include <evhttp.h>
0
 
0
+#include <netdb.h>
0
+#include <evdns.h>
0
+
0
 #import <Foundation/Foundation.h>
0
 #import <Nu/Nu.h>
0
 
0
@@ -160,9 +163,11 @@ static void nunja_response_helper(struct evhttp_request *req, int code, NSString
0
 - (void) handleRequest:(NunjaRequest *)request;
0
 @end
0
 
0
+struct event_base *event_base;
0
+
0
 @interface Nunja : NSObject
0
 {
0
- struct event_base *event_base;
0
+
0
     struct evhttp *httpd;
0
     id<NSObject,NunjaDelegate> delegate;
0
 }
0
@@ -204,6 +209,7 @@ static void nunja_request_handler(struct evhttp_request *req, void *nunja_pointe
0
 {
0
     [super init];
0
     event_base = event_init();
0
+ evdns_init();
0
     httpd = evhttp_new(event_base);
0
     evhttp_set_gencb(httpd, nunja_request_handler, self);
0
     delegate = nil;
0
@@ -238,4 +244,106 @@ static void nunja_request_handler(struct evhttp_request *req, void *nunja_pointe
0
     delegate = d;
0
 }
0
 
0
+@class NuBlock;
0
+@class NuCell;
0
+
0
+static void nunja_dns_gethostbyname_cb(int result, char type, int count, int ttl, void *addresses, void *arg)
0
+{
0
+ id address = nil;
0
+
0
+ if (result == DNS_ERR_TIMEOUT) {
0
+ fprintf(stdout, "[Timed out] ");
0
+ goto out;
0
+ }
0
+
0
+ if (result != DNS_ERR_NONE) {
0
+ fprintf(stdout, "[Error code %d] ", result);
0
+ goto out;
0
+ }
0
+
0
+ fprintf(stderr, "type: %d, count: %d, ttl: %d: ", type, count, ttl);
0
+
0
+ switch (type) {
0
+ case DNS_IPv4_A:
0
+ {
0
+ struct in_addr *in_addrs = addresses;
0
+ int i;
0
+ /* a resolution that's not valid does not help */
0
+ if (ttl < 0)
0
+ goto out;
0
+ if (count == 0)
0
+ goto out;
0
+ address = [NSString stringWithFormat:@"%s", inet_ntoa(in_addrs[0])];
0
+ break;
0
+ }
0
+ case DNS_PTR:
0
+ /* may get at most one PTR */
0
+ if (count != 1)
0
+ goto out;
0
+
0
+ fprintf(stderr, "%s ", *(char **)addresses);
0
+ break;
0
+ default:
0
+ goto out;
0
+ }
0
+ out:
0
+ {
0
+ NuBlock *block = (NuBlock *) arg;
0
+ NuCell *args = [[NuCell alloc] init];
0
+ [args setCar:address];
0
+ [block evalWithArguments:args context:nil];
0
+ [block release];
0
+ }
0
+}
0
+
0
+- (void) resolveDomainName:(NSString *) name andDo:(NuBlock *) block
0
+{
0
+ [block retain];
0
+ evdns_resolve_ipv4([name cStringUsingEncoding:NSUTF8StringEncoding], 0, nunja_dns_gethostbyname_cb, block);
0
+}
0
+
0
+void
0
+nu_http_request_done(struct evhttp_request *req, void *arg)
0
+{
0
+ printf("received %d\n", (int) arg);
0
+ NSData *data = nil;
0
+ if (req->response_code != HTTP_OK) {
0
+ fprintf(stderr, "FAILED to get OK\n");
0
+ }
0
+ else if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) {
0
+ fprintf(stderr, "FAILED to find Content-Type\n");
0
+ }
0
+ else {
0
+ data = [NSData dataWithBytes:EVBUFFER_DATA(req->input_buffer) length:EVBUFFER_LENGTH(req->input_buffer)];
0
+ }
0
+ NuBlock *block = (NuBlock *) arg;
0
+ NuCell *args = [[NuCell alloc] init];
0
+ [args setCar:data];
0
+ [block evalWithArguments:args context:nil];
0
+ [block release];
0
+ // leaking...
0
+ //evhttp_connection_free(req->evcon);
0
+}
0
+
0
+- (void) getResourceFromHost:(NSString *) host address:(NSString *) address port:(int)port path:(NSString *)path andDo:(NuBlock *) block
0
+{
0
+ struct evhttp_connection *evcon = evhttp_connection_new([address cStringUsingEncoding:NSUTF8StringEncoding], port);
0
+ if (evcon == NULL) {
0
+ fprintf(stdout, "FAILED to connect\n");
0
+ return;
0
+ }
0
+ /*
0
+ * At this point, we want to schedule a request to the HTTP
0
+ * server using our make request method.
0
+ */
0
+ [block retain];
0
+ struct evhttp_request *req = evhttp_request_new(nu_http_request_done, block);
0
+ /* Add the information that we care about */
0
+ evhttp_add_header(req->output_headers, "Host", [host cStringUsingEncoding:NSUTF8StringEncoding]);
0
+ /* We give ownership of the request to the connection */
0
+ if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, [path cStringUsingEncoding:NSUTF8StringEncoding]) == -1) {
0
+ fprintf(stdout, "FAILED to make the request \n");
0
+ }
0
+}
0
+
0
 @end
...
154
155
156
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
0
@@ -154,3 +154,30 @@ END)
0
                         else (size doubleValue)))
0
      (set data (NSData dataWithSize:(* megabytes 1024 1024))))
0
 
0
+(get (regex -"/dns/(.*)")
0
+ (puts (REQUEST description))
0
+ (set hostname (MATCH groupAtIndex:1))
0
+ ($nunja resolveDomainName:hostname andDo:
0
+ (do (address)
0
+ (if address
0
+ (REQUEST respondWithString:"resolved #{hostname} as #{address}")
0
+ else
0
+ (REQUEST respondWithString:"unable to resolve #{hostname}"))))
0
+ nil)
0
+
0
+(get (regex -"/proxy/([^\/]+)/(.*)")
0
+ (puts (REQUEST description))
0
+ (set host (MATCH groupAtIndex:1))
0
+ (set path (+ "/" (MATCH groupAtIndex:2)))
0
+ ($nunja resolveDomainName:host andDo:
0
+ (do (address)
0
+ (if address
0
+ (then ($nunja getResourceFromHost:host address:address port:80 path:path andDo:
0
+ (do (data)
0
+ (if data
0
+ (then (REQUEST respondWithData:data))
0
+ (else (REQUEST respondWithString:"unable to load #{path}"))))))
0
+ (else (REQUEST respondWithString:"unable to resolve host #{host}")))))
0
+ nil)
0
+
0
+

Comments

    No one has commented yet.