The R Project SVN R

Rev

Rev 49887 | Rev 51563 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 49887 Rev 51562
Line 155... Line 155...
155
#define METHOD_POST 1
155
#define METHOD_POST 1
156
#define METHOD_GET  2
156
#define METHOD_GET  2
157
#define METHOD_HEAD 3
157
#define METHOD_HEAD 3
158
 
158
 
159
/* attributes of a connection/worker */
159
/* attributes of a connection/worker */
160
#define CONNECTION_CLOSE 0x01 /* Connection: close response behavior is requested */
160
#define CONNECTION_CLOSE  0x01 /* Connection: close response behavior is requested */
161
#define HOST_HEADER      0x02 /* headers contained Host: header (required for HTTP/1.1) */
161
#define HOST_HEADER       0x02 /* headers contained Host: header (required for HTTP/1.1) */
162
#define HTTP_1_0         0x04 /* the client requested HTTP/1.0 */
162
#define HTTP_1_0          0x04 /* the client requested HTTP/1.0 */
163
#define CONTENT_LENGTH   0x08 /* Content-length: was specified in the headers */
163
#define CONTENT_LENGTH    0x08 /* Content-length: was specified in the headers */
164
#define THREAD_OWNED     0x10 /* the worker is owned by a thread and cannot removed */
164
#define THREAD_OWNED      0x10 /* the worker is owned by a thread and cannot removed */
165
#define THREAD_DISPOSE   0x20 /* the thread should dispose of the worker */
165
#define THREAD_DISPOSE    0x20 /* the thread should dispose of the worker */
-
 
166
#define CONTENT_TYPE      0x40 /* message has a specific content type set */
-
 
167
#define CONTENT_FORM_UENC 0x80 /* message content type is application/x-www-form-urlencoded */
166
 
168
 
167
/* --- connection/worker structure holding all data for an active connection --- */
169
/* --- connection/worker structure holding all data for an active connection --- */
168
typedef struct httpd_conn {
170
typedef struct httpd_conn {
169
    SOCKET sock;         /* client socket */
171
    SOCKET sock;         /* client socket */
170
    struct in_addr peer; /* IP address of the peer */
172
    struct in_addr peer; /* IP address of the peer */
Line 173... Line 175...
173
#else
175
#else
174
    InputHandler *ih;    /* worker input handler */
176
    InputHandler *ih;    /* worker input handler */
175
#endif
177
#endif
176
    char line_buf[LINE_BUF_SIZE];  /* line buffer (used for request and headers) */
178
    char line_buf[LINE_BUF_SIZE];  /* line buffer (used for request and headers) */
177
    char *url, *body;              /* URL and request body */
179
    char *url, *body;              /* URL and request body */
-
 
180
    char *content_type;            /* content type (if set) */
178
    unsigned int line_pos, body_pos, content_length; /* positions in the buffers and desired content length */
181
    unsigned int line_pos, body_pos, content_length; /* positions in the buffers and desired content length */
179
    char part, method, attr;       /* request part, method and connection attributes */
182
    char part, method, attr;       /* request part, method and connection attributes */
180
} httpd_conn_t;
183
} httpd_conn_t;
181
 
184
 
182
/* returns the HTTP/x.x string for a given connection - we support 1.0 and 1.1 only */
-
 
183
#define HTTP_SIG(C) ((((C)->attr & HTTP_1_0) == 0) ? "HTTP/1.1" : "HTTP/1.0")
-
 
184
 
-
 
185
#define IS_HTTP_1_1(C) (((C)->attr & HTTP_1_0) == 0)
185
#define IS_HTTP_1_1(C) (((C)->attr & HTTP_1_0) == 0)
186
 
186
 
-
 
187
/* returns the HTTP/x.x string for a given connection - we support 1.0 and 1.1 only */
-
 
188
#define HTTP_SIG(C) (IS_HTTP_1_1(C) ? "HTTP/1.1" : "HTTP/1.0")
-
 
189
 
187
/* --- static list of currently activbe workers --- */
190
/* --- static list of currently active workers --- */
188
static httpd_conn_t *workers[MAX_WORKERS];
191
static httpd_conn_t *workers[MAX_WORKERS];
189
 
192
 
190
/* --- flag determining whether one-time initialization is yet to be performed --- */
193
/* --- flag determining whether one-time initialization is yet to be performed --- */
191
static int needs_init = 1;
194
static int needs_init = 1;
192
 
195
 
Line 234... Line 237...
234
    if (c->body) {
237
    if (c->body) {
235
	free(c->body);
238
	free(c->body);
236
	c->body = NULL;
239
	c->body = NULL;
237
    }
240
    }
238
 
241
 
-
 
242
    if (c->content_type) {
-
 
243
	free(c->content_type);
-
 
244
	c->content_type = NULL;
-
 
245
    }
-
 
246
    
239
    if (c->sock != INVALID_SOCKET) {
247
    if (c->sock != INVALID_SOCKET) {
240
	closesocket(c->sock);
248
	closesocket(c->sock);
241
	c->sock = INVALID_SOCKET;
249
	c->sock = INVALID_SOCKET;
242
    }
250
    }
243
}
251
}
Line 298... Line 306...
298
    return 0;
306
    return 0;
299
}
307
}
300
 
308
 
301
/* sends HTTP/x.x plus the text (which should be of the form " XXX ...") */
309
/* sends HTTP/x.x plus the text (which should be of the form " XXX ...") */
302
static int send_http_response(httpd_conn_t *c, const char *text) {
310
static int send_http_response(httpd_conn_t *c, const char *text) {
-
 
311
    char buf[96];
303
    const char *s = HTTP_SIG(c);
312
    const char *s = HTTP_SIG(c);
-
 
313
    int l = strlen(text);
-
 
314
    /* reduce the number of packets by sending the payload en-block from buf */
-
 
315
    if (l < sizeof(buf) - 10) {
-
 
316
	strcpy(buf, s);
-
 
317
	strcpy(buf + 8, text);
-
 
318
	return send_response(c->sock, buf, l + 8);
-
 
319
    }
304
    if (send(c->sock, s, 8, 0) < 8) return -1;
320
    if (send(c->sock, s, 8, 0) < 8) return -1;
305
    return send_response(c->sock, text, strlen(text));
321
    return send_response(c->sock, text, strlen(text));
306
}
322
}
307
 
323
 
308
/* decode URI in place (decoding never expands) */
324
/* decode URI in place (decoding never expands) */
Line 381... Line 397...
381
    setAttrib(res, R_NamesSymbol, names);
397
    setAttrib(res, R_NamesSymbol, names);
382
    UNPROTECT(2);
398
    UNPROTECT(2);
383
    return res;
399
    return res;
384
}
400
}
385
 
401
 
-
 
402
/* create an object representing the request body. It is NULL if the body is empty (or zero length).
-
 
403
 * In the case of a URL encoded form it will have the same shape as the query string (named string vector).
-
 
404
 * In all other cases it will be a raw vector with a "content-type" attribute (if specified in the headers) */
-
 
405
static SEXP parse_request_body(httpd_conn_t *c) {
-
 
406
    if (!c || !c->body) return R_NilValue;
-
 
407
 
-
 
408
    if (c->attr & CONTENT_FORM_UENC) { /* URL encoded form - return parsed form */
-
 
409
	c->body[c->content_length] = 0; /* the body is guaranteed to have an extra byte for the termination */
-
 
410
	return parse_query(c->body);
-
 
411
    } else { /* something else - pass it as a raw vector */
-
 
412
	SEXP res = PROTECT(Rf_allocVector(RAWSXP, c->content_length));
-
 
413
	if (c->content_length)
-
 
414
	    memcpy(RAW(res), c->body, c->content_length);
-
 
415
	if (c->content_type) /* attach the content type so it can be interpreted */
-
 
416
	    setAttrib(res, install("content-type"), mkString(c->content_type));
-
 
417
	UNPROTECT(1);
-
 
418
	return res;
-
 
419
    }
-
 
420
}
-
 
421
 
386
#ifdef WIN32
422
#ifdef WIN32
387
/* on Windows we have to guarantee that process_request is performed
423
/* on Windows we have to guarantee that process_request is performed
388
 * on the main thread, so we have to dispatch it through a message */
424
 * on the main thread, so we have to dispatch it through a message */
389
static void process_request_main_thread(httpd_conn_t *c);
425
static void process_request_main_thread(httpd_conn_t *c);
390
 
426
 
Line 420... Line 456...
420
    if (*s) {
456
    if (*s) {
421
	*(s++) = 0;
457
	*(s++) = 0;
422
	query = s;
458
	query = s;
423
    }
459
    }
424
    uri_decode(c->url); /* decode the path part */
460
    uri_decode(c->url); /* decode the path part */
425
    { /* construct try(httpd(url, query), silent=TRUE) */
461
    {   /* construct "try(httpd(url, query, body), silent=TRUE)" */
426
	SEXP sTrue = PROTECT(ScalarLogical(TRUE));
462
	SEXP sTrue = PROTECT(ScalarLogical(TRUE));
-
 
463
	SEXP y, x = PROTECT(lang3(
-
 
464
				  install("try"),
-
 
465
				  LCONS(install("httpd"),
427
	SEXP y, x = PROTECT(lang3(install("try"), LCONS(install("httpd"), CONS(mkString(c->url), CONS(query ? parse_query(query) : R_NilValue, R_NilValue))), sTrue));
466
					list3(mkString(c->url), query ? parse_query(query) : R_NilValue, parse_request_body(c))),
-
 
467
				  sTrue));
428
	SET_TAG(CDR(CDR(x)), install("silent"));
468
	SET_TAG(CDR(CDR(x)), install("silent"));
429
	DBG(Rprintf("eval(try(httpd('%s'),silent=TRUE))\n", c->url));
469
	DBG(Rprintf("eval(try(httpd('%s'),silent=TRUE))\n", c->url));
-
 
470
 
-
 
471
	/* evaluate the above in the tools namespace */
430
	x = PROTECT(eval(x, R_FindNamespace(mkString("tools"))));
472
	x = PROTECT(eval(x, R_FindNamespace(mkString("tools"))));
431
 
473
 
432
	/* the result is expected to have one of the following forms:
474
	/* the result is expected to have one of the following forms:
433
 
475
 
434
	   a) character vector of length 1 => error (possibly from try),
476
	   a) character vector of length 1 => error (possibly from try),
Line 515... Line 557...
515
		    fseek(f, 0, SEEK_SET);
557
		    fseek(f, 0, SEEK_SET);
516
		    sprintf(buf, "\r\nContent-length: %ld\r\n\r\n", fsz);
558
		    sprintf(buf, "\r\nContent-length: %ld\r\n\r\n", fsz);
517
		    send_response(c->sock, buf, strlen(buf));
559
		    send_response(c->sock, buf, strlen(buf));
518
		    if (c->method != METHOD_HEAD) {
560
		    if (c->method != METHOD_HEAD) {
519
			fbuf = (char*) malloc(32768);
561
			fbuf = (char*) malloc(32768);
-
 
562
			if (fbuf) {
520
			while (fsz > 0 && !feof(f)) {
563
			    while (fsz > 0 && !feof(f)) {
521
			    int rd = (fsz > 32768) ? 32768 : fsz;
564
				int rd = (fsz > 32768) ? 32768 : fsz;
522
			    if (fread(fbuf, 1, rd, f) != rd) {
565
				if (fread(fbuf, 1, rd, f) != rd) {
523
				free(fbuf);
566
				    free(fbuf);
524
				UNPROTECT(3);
567
				    UNPROTECT(3);
525
				c->attr |= CONNECTION_CLOSE;
568
				    c->attr |= CONNECTION_CLOSE;
526
				return;
569
				    return;
-
 
570
				}
-
 
571
				send_response(c->sock, fbuf, rd);
-
 
572
				fsz -= rd;
527
			    }
573
			    }
528
			    send_response(c->sock, fbuf, rd);
574
			    free(fbuf);
-
 
575
			} else { /* allocation error - get out */
-
 
576
			    UNPROTECT(3);
-
 
577
			    c->attr |= CONNECTION_CLOSE;
529
			    fsz -= rd;
578
			    return;
530
			}
579
			}
531
			free(fbuf);
-
 
532
		    }
580
		    }
533
		    fclose(f);
581
		    fclose(f);
534
		    UNPROTECT(3);
582
		    UNPROTECT(3);
535
		    fin_request(c);
583
		    fin_request(c);
536
		    return;
584
		    return;
Line 624... Line 672...
624
		DBG(printf(" end of request, moving to body\n"));
672
		DBG(printf(" end of request, moving to body\n"));
625
		if (!(c->attr & HTTP_1_0) && !(c->attr & HOST_HEADER)) { /* HTTP/1.1 mandates Host: header */
673
		if (!(c->attr & HTTP_1_0) && !(c->attr & HOST_HEADER)) { /* HTTP/1.1 mandates Host: header */
626
		    send_http_response(c, " 400 Bad Request (Host: missing)\r\nConnection: close\r\n\r\n");
674
		    send_http_response(c, " 400 Bad Request (Host: missing)\r\nConnection: close\r\n\r\n");
627
		    remove_worker(c);
675
		    remove_worker(c);
628
		    return;
676
		    return;
629
 
-
 
630
		}
677
		}
631
		if (c->attr & CONTENT_LENGTH) {
678
		if (c->attr & CONTENT_LENGTH && c->content_length) {
632
		    if (c->content_length)
679
		    c->body = (char*) malloc(c->content_length + 1); /* allocate an extra termination byte */
633
			c->body = (char*) malloc(c->content_length);
680
		    if (!c->body) { /* uh oh - out of memory - refuse */
-
 
681
			send_http_response(c, " 413 Request Entity Too Large (request body too big)\r\nConnection: close\r\n\r\n");
-
 
682
			remove_worker(c);
-
 
683
			return;
-
 
684
		    }
634
		}
685
		}
635
		c->body_pos = 0;
686
		c->body_pos = 0;
636
		c->part = PART_BODY;
687
		c->part = PART_BODY;
637
		if (s[0] == '\r') s++;
688
		if (s[0] == '\r') s++;
638
		s++;
689
		s++;
Line 651... Line 702...
651
			return;
702
			return;
652
		    }
703
		    }
653
		    /* keep-alive - reset the worker so it can process a new request */
704
		    /* keep-alive - reset the worker so it can process a new request */
654
		    if (c->url) { free(c->url); c->url = NULL; }
705
		    if (c->url) { free(c->url); c->url = NULL; }
655
		    if (c->body) { free(c->body); c->body = NULL; }
706
		    if (c->body) { free(c->body); c->body = NULL; }
-
 
707
		    if (c->content_type) { free(c->content_type); c->content_type = NULL; }
656
		    c->body_pos = 0;
708
		    c->body_pos = 0;
657
		    c->method = 0;
709
		    c->method = 0;
658
		    c->part = PART_REQUEST;
710
		    c->part = PART_REQUEST;
659
		    c->attr = 0;
711
		    c->attr = 0;
660
		    c->content_length = 0;
712
		    c->content_length = 0;
Line 719... Line 771...
719
			    DBG(printf("header '%s' => '%s'\n", bol, k));
771
			    DBG(printf("header '%s' => '%s'\n", bol, k));
720
			    if (!strcmp(bol, "content-length")) {
772
			    if (!strcmp(bol, "content-length")) {
721
				c->attr |= CONTENT_LENGTH;
773
				c->attr |= CONTENT_LENGTH;
722
				c->content_length = atoi(k);
774
				c->content_length = atoi(k);
723
			    }
775
			    }
-
 
776
			    if (!strcmp(bol, "content-type")) {
-
 
777
				char *l = k;
-
 
778
				while (*l) { if (*l >= 'A' && *l <= 'Z') *l |= 0x20; l++; }
-
 
779
				c->attr |= CONTENT_TYPE;
-
 
780
				if (c->content_type) free(c->content_type);
-
 
781
				c->content_type = strdup(k);
-
 
782
				if (!strncmp(k, "application/x-www-form-urlencoded", 33))
-
 
783
				    c->attr |= CONTENT_FORM_UENC;
-
 
784
			    }
724
			    if (!strcmp(bol, "host"))
785
			    if (!strcmp(bol, "host"))
725
				c->attr |= HOST_HEADER;
786
				c->attr |= HOST_HEADER;
726
			    if (!strcmp(bol, "connection")) {
787
			    if (!strcmp(bol, "connection")) {
727
				char *l = k;
788
				char *l = k;
728
				while (*l) { if (*l >= 'A' && *l <= 'Z') *l |= 0x20; l++; }
789
				while (*l) { if (*l >= 'A' && *l <= 'Z') *l |= 0x20; l++; }
Line 758... Line 819...
758
		return;
819
		return;
759
	    }
820
	    }
760
	    /* keep-alive - reset the worker so it can process a new request */
821
	    /* keep-alive - reset the worker so it can process a new request */
761
	    if (c->url) { free(c->url); c->url = NULL; }
822
	    if (c->url) { free(c->url); c->url = NULL; }
762
	    if (c->body) { free(c->body); c->body = NULL; }
823
	    if (c->body) { free(c->body); c->body = NULL; }
-
 
824
	    if (c->content_type) { free(c->content_type); c->content_type = NULL; }
763
	    c->line_pos = 0; c->body_pos = 0;
825
	    c->line_pos = 0; c->body_pos = 0;
764
	    c->method = 0;
826
	    c->method = 0;
765
	    c->part = PART_REQUEST;
827
	    c->part = PART_REQUEST;
766
	    c->attr = 0;
828
	    c->attr = 0;
767
	    c->content_length = 0;
829
	    c->content_length = 0;
Line 793... Line 855...
793
		    c->line_pos -= sh;
855
		    c->line_pos -= sh;
794
		}
856
		}
795
		/* keep-alive - reset the worker so it can process a new request */
857
		/* keep-alive - reset the worker so it can process a new request */
796
		if (c->url) { free(c->url); c->url = NULL; }
858
		if (c->url) { free(c->url); c->url = NULL; }
797
		if (c->body) { free(c->body); c->body = NULL; }
859
		if (c->body) { free(c->body); c->body = NULL; }
-
 
860
		if (c->content_type) { free(c->content_type); c->content_type = NULL; }
798
		c->body_pos = 0;
861
		c->body_pos = 0;
799
		c->method = 0;
862
		c->method = 0;
800
		c->part = PART_REQUEST;
863
		c->part = PART_REQUEST;
801
		c->attr = 0;
864
		c->attr = 0;
802
		c->content_length = 0;
865
		c->content_length = 0;
803
		return;
866
		return;
804
	    }
867
	    }
805
	}
868
	}
806
	n = recv(c->sock, c->line_buf + c->line_pos, LINE_BUF_SIZE - c->line_pos - 1, 0);
869
	n = recv(c->sock, c->line_buf + c->line_pos, LINE_BUF_SIZE - c->line_pos - 1, 0);
807
	if (n < 0) { /* error, scrape this worker */
870
	if (n < 0) { /* error, scrap this worker */
808
	    remove_worker(c);
871
	    remove_worker(c);
809
	    return;
872
	    return;
810
	}
873
	}
811
	if (n == 0) { /* connection closed -> try to process and then remove */
874
	if (n == 0) { /* connection closed -> try to process and then remove */
812
	    process_request(c);
875
	    process_request(c);