| 48990 |
urbaneks |
1 |
/*
|
|
|
2 |
* R : A Computer Language for Statistical Data Analysis
|
| 85799 |
kalibera |
3 |
* Copyright (C) 2009-2024 The R Core Team.
|
| 48990 |
urbaneks |
4 |
*
|
|
|
5 |
* This program is free software; you can redistribute it and/or modify
|
|
|
6 |
* it under the terms of the GNU General Public License as published by
|
|
|
7 |
* the Free Software Foundation; either version 2 of the License, or
|
|
|
8 |
* (at your option) any later version.
|
|
|
9 |
*
|
|
|
10 |
* This program is distributed in the hope that it will be useful,
|
|
|
11 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
12 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
13 |
* GNU General Public License for more details.
|
|
|
14 |
*
|
|
|
15 |
* You should have received a copy of the GNU General Public License
|
|
|
16 |
* along with this program; if not, a copy is available at
|
| 68949 |
ripley |
17 |
* https://www.R-project.org/Licenses/
|
| 48990 |
urbaneks |
18 |
*/
|
|
|
19 |
|
|
|
20 |
/* This is a small HTTP server that serves requests by evaluating
|
|
|
21 |
* the httpd() function and passing the result to the browser. */
|
|
|
22 |
|
| 86514 |
kalibera |
23 |
/* Note that the server runs partially on the main R thread (it has to,
|
|
|
24 |
because it uses R), and it also sends the response mostly from the
|
|
|
25 |
main thread. It is thus not possible to use the server from the R thread
|
|
|
26 |
itself, e.g. via download.file(), because of a deadlock between the
|
|
|
27 |
server and the client, when the data isn't sent in a single chunk.
|
|
|
28 |
On Unix, the deadlock could also happen when the client is sending a
|
|
|
29 |
request to the server (and the request isn't sent in a single chunk).
|
|
|
30 |
|
|
|
31 |
This cannot happen with the intended use of this server, when it is used
|
|
|
32 |
to serve help pages to an external client (a web browser).
|
|
|
33 |
*/
|
|
|
34 |
|
| 48990 |
urbaneks |
35 |
/* Example:
|
| 49434 |
ripley |
36 |
httpd <- function(path,query=NULL,...) {
|
|
|
37 |
cat("Request for:", path,"\n"); print(query);
|
|
|
38 |
list(paste("Hello, <b>world</b>!<p>You asked for \"",path,"\".",sep=''))
|
|
|
39 |
}
|
| 48990 |
urbaneks |
40 |
.Internal(startHTTPD("127.0.0.1",8080))
|
|
|
41 |
*/
|
|
|
42 |
|
|
|
43 |
/* size of the line buffer for each worker (request and header only)
|
|
|
44 |
* requests that have longer headers will be rejected with 413 */
|
|
|
45 |
#define LINE_BUF_SIZE 1024
|
|
|
46 |
|
|
|
47 |
/* maximum number of active workers (parallel connections)
|
|
|
48 |
* when exceeded the server closes new connections */
|
|
|
49 |
#define MAX_WORKERS 32
|
|
|
50 |
|
|
|
51 |
|
|
|
52 |
/* --- Rhttpd implementation --- */
|
|
|
53 |
|
|
|
54 |
#ifdef HAVE_CONFIG_H
|
|
|
55 |
#include <config.h>
|
|
|
56 |
#endif
|
|
|
57 |
|
|
|
58 |
#include <Defn.h>
|
|
|
59 |
#include <Fileio.h>
|
|
|
60 |
#include <Rconnections.h>
|
|
|
61 |
|
|
|
62 |
#include <stdlib.h>
|
|
|
63 |
#include <stdio.h>
|
|
|
64 |
#include <string.h>
|
|
|
65 |
|
|
|
66 |
#include <Rmodules/Rinternet.h>
|
|
|
67 |
|
|
|
68 |
#define HttpdServerActivity 8
|
|
|
69 |
#define HttpdWorkerActivity 9
|
|
|
70 |
|
| 84215 |
maechler |
71 |
/* this is originally from sisock.h - system independent sockets */
|
| 48990 |
urbaneks |
72 |
|
| 65805 |
ripley |
73 |
#ifndef _WIN32
|
| 49434 |
ripley |
74 |
# include <R_ext/eventloop.h>
|
|
|
75 |
# include <sys/types.h>
|
|
|
76 |
# ifdef HAVE_UNISTD_H
|
|
|
77 |
# include <unistd.h>
|
|
|
78 |
# endif
|
| 68899 |
ripley |
79 |
# include <netdb.h>
|
|
|
80 |
# include <sys/socket.h>
|
|
|
81 |
# include <netinet/in.h>
|
|
|
82 |
# include <arpa/inet.h>
|
| 49434 |
ripley |
83 |
# include <errno.h>
|
| 48990 |
urbaneks |
84 |
|
| 49434 |
ripley |
85 |
# define sockerrno errno
|
|
|
86 |
# define SOCKET int
|
|
|
87 |
# define INVALID_SOCKET (-1)
|
|
|
88 |
# define closesocket(A) close(A)
|
|
|
89 |
# define initsocks()
|
|
|
90 |
# define donesocks()
|
| 48990 |
urbaneks |
91 |
#else
|
| 49434 |
ripley |
92 |
/* --- Windows-only --- */
|
| 85799 |
kalibera |
93 |
# include <winsock2.h>
|
| 49434 |
ripley |
94 |
# include <windows.h>
|
|
|
95 |
# include <string.h>
|
| 48990 |
urbaneks |
96 |
|
| 49434 |
ripley |
97 |
# define sockerrno WSAGetLastError()
|
| 48990 |
urbaneks |
98 |
|
|
|
99 |
|
|
|
100 |
static int initsocks(void)
|
|
|
101 |
{
|
|
|
102 |
WSADATA dt;
|
| 85799 |
kalibera |
103 |
/* initialize WinSock 2.2 */
|
|
|
104 |
WORD wVers = MAKEWORD(2, 2);
|
|
|
105 |
return (WSAStartup(wVers, &dt)) ? -1 : 0;
|
| 48990 |
urbaneks |
106 |
}
|
|
|
107 |
|
| 49434 |
ripley |
108 |
# define donesocks() WSACleanup()
|
| 48990 |
urbaneks |
109 |
typedef int socklen_t;
|
|
|
110 |
|
| 65805 |
ripley |
111 |
#endif /* _WIN32 */
|
| 48990 |
urbaneks |
112 |
|
|
|
113 |
/* --- system-independent part --- */
|
|
|
114 |
|
|
|
115 |
#define SA struct sockaddr
|
|
|
116 |
#define SAIN struct sockaddr_in
|
|
|
117 |
|
|
|
118 |
static struct sockaddr *build_sin(struct sockaddr_in *sa, const char *ip, int port) {
|
|
|
119 |
memset(sa, 0, sizeof(struct sockaddr_in));
|
|
|
120 |
sa->sin_family = AF_INET;
|
|
|
121 |
sa->sin_port = htons(port);
|
|
|
122 |
sa->sin_addr.s_addr = (ip) ? inet_addr(ip) : htonl(INADDR_ANY);
|
|
|
123 |
return (struct sockaddr*)sa;
|
|
|
124 |
}
|
|
|
125 |
|
|
|
126 |
/* --- END of sisock.h --- */
|
|
|
127 |
|
|
|
128 |
/* debug output - change the DBG(X) X to enable debugging output */
|
|
|
129 |
#define DBG(X)
|
|
|
130 |
|
|
|
131 |
/* --- httpd --- */
|
|
|
132 |
|
|
|
133 |
#define PART_REQUEST 0
|
|
|
134 |
#define PART_HEADER 1
|
|
|
135 |
#define PART_BODY 2
|
|
|
136 |
|
| 62724 |
urbaneks |
137 |
#define METHOD_POST 1
|
|
|
138 |
#define METHOD_GET 2
|
|
|
139 |
#define METHOD_HEAD 3
|
|
|
140 |
#define METHOD_OTHER 8 /* for custom requests only */
|
| 48990 |
urbaneks |
141 |
|
|
|
142 |
/* attributes of a connection/worker */
|
| 51562 |
urbaneks |
143 |
#define CONNECTION_CLOSE 0x01 /* Connection: close response behavior is requested */
|
|
|
144 |
#define HOST_HEADER 0x02 /* headers contained Host: header (required for HTTP/1.1) */
|
|
|
145 |
#define HTTP_1_0 0x04 /* the client requested HTTP/1.0 */
|
|
|
146 |
#define CONTENT_LENGTH 0x08 /* Content-length: was specified in the headers */
|
|
|
147 |
#define THREAD_OWNED 0x10 /* the worker is owned by a thread and cannot removed */
|
|
|
148 |
#define THREAD_DISPOSE 0x20 /* the thread should dispose of the worker */
|
|
|
149 |
#define CONTENT_TYPE 0x40 /* message has a specific content type set */
|
|
|
150 |
#define CONTENT_FORM_UENC 0x80 /* message content type is application/x-www-form-urlencoded */
|
| 48990 |
urbaneks |
151 |
|
| 54055 |
urbaneks |
152 |
struct buffer {
|
|
|
153 |
struct buffer *next, *prev;
|
| 59184 |
ripley |
154 |
size_t size, length;
|
| 54055 |
urbaneks |
155 |
char data[1];
|
|
|
156 |
};
|
|
|
157 |
|
| 78421 |
kalibera |
158 |
/*
|
|
|
159 |
All processing inside R is executed on the main R thread via
|
|
|
160 |
R_ProcessEvents (on Unix via event handlers, on Windows via a message
|
|
|
161 |
window).
|
|
|
162 |
|
|
|
163 |
We still have to protect re-entrance and not continue processing if there
|
|
|
164 |
is a worker inside R already. If we did not then another client connection
|
| 59338 |
urbaneks |
165 |
would trigger handler and pile up eval on top of the stack, leading to
|
| 78421 |
kalibera |
166 |
exhaustion very quickly and a big mess.
|
|
|
167 |
*/
|
|
|
168 |
#ifdef _WIN32
|
|
|
169 |
static HANDLE process_request_mutex;
|
|
|
170 |
#else
|
| 59338 |
urbaneks |
171 |
static int in_process;
|
| 78421 |
kalibera |
172 |
#endif
|
| 59338 |
urbaneks |
173 |
|
| 48990 |
urbaneks |
174 |
/* --- connection/worker structure holding all data for an active connection --- */
|
|
|
175 |
typedef struct httpd_conn {
|
|
|
176 |
SOCKET sock; /* client socket */
|
|
|
177 |
struct in_addr peer; /* IP address of the peer */
|
| 65805 |
ripley |
178 |
#ifdef _WIN32
|
| 48990 |
urbaneks |
179 |
HANDLE thread; /* worker thread */
|
|
|
180 |
#else
|
|
|
181 |
InputHandler *ih; /* worker input handler */
|
|
|
182 |
#endif
|
|
|
183 |
char line_buf[LINE_BUF_SIZE]; /* line buffer (used for request and headers) */
|
|
|
184 |
char *url, *body; /* URL and request body */
|
| 51562 |
urbaneks |
185 |
char *content_type; /* content type (if set) */
|
| 59184 |
ripley |
186 |
size_t line_pos, body_pos; /* positions in the buffers */
|
| 52878 |
urbaneks |
187 |
long content_length; /* desired content length */
|
| 48990 |
urbaneks |
188 |
char part, method, attr; /* request part, method and connection attributes */
|
| 54055 |
urbaneks |
189 |
struct buffer *headers; /* buffer holding header lines */
|
| 48990 |
urbaneks |
190 |
} httpd_conn_t;
|
|
|
191 |
|
| 51562 |
urbaneks |
192 |
#define IS_HTTP_1_1(C) (((C)->attr & HTTP_1_0) == 0)
|
|
|
193 |
|
| 49887 |
urbaneks |
194 |
/* returns the HTTP/x.x string for a given connection - we support 1.0 and 1.1 only */
|
| 51562 |
urbaneks |
195 |
#define HTTP_SIG(C) (IS_HTTP_1_1(C) ? "HTTP/1.1" : "HTTP/1.0")
|
| 49887 |
urbaneks |
196 |
|
| 51562 |
urbaneks |
197 |
/* --- static list of currently active workers --- */
|
| 48990 |
urbaneks |
198 |
static httpd_conn_t *workers[MAX_WORKERS];
|
|
|
199 |
|
|
|
200 |
/* --- flag determining whether one-time initialization is yet to be performed --- */
|
|
|
201 |
static int needs_init = 1;
|
|
|
202 |
|
| 65805 |
ripley |
203 |
#ifdef _WIN32
|
| 48990 |
urbaneks |
204 |
#define WM_RHTTP_CALLBACK ( WM_USER + 1 )
|
|
|
205 |
static HWND message_window;
|
| 49434 |
ripley |
206 |
static LRESULT CALLBACK
|
|
|
207 |
RhttpdWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
| 48990 |
urbaneks |
208 |
#ifndef HWND_MESSAGE
|
|
|
209 |
#define HWND_MESSAGE ((HWND)-3) /* NOTE: this is supported by W2k/XP and up only! */
|
|
|
210 |
#endif
|
|
|
211 |
#endif
|
|
|
212 |
|
| 82931 |
ripley |
213 |
static void first_init(void)
|
| 49434 |
ripley |
214 |
{
|
| 48990 |
urbaneks |
215 |
initsocks();
|
| 65805 |
ripley |
216 |
#ifdef _WIN32
|
| 49434 |
ripley |
217 |
/* create a dummy message-only window for synchronization with the
|
|
|
218 |
* main event loop */
|
| 48990 |
urbaneks |
219 |
HINSTANCE instance = GetModuleHandle(NULL);
|
|
|
220 |
LPCTSTR class = "Rhttpd";
|
| 49434 |
ripley |
221 |
WNDCLASS wndclass = { 0, RhttpdWindowProc, 0, 0, instance, NULL, 0, 0,
|
|
|
222 |
NULL, class };
|
| 48990 |
urbaneks |
223 |
RegisterClass(&wndclass);
|
| 49434 |
ripley |
224 |
message_window = CreateWindow(class, "Rhttpd", 0, 1, 1, 1, 1,
|
|
|
225 |
HWND_MESSAGE, NULL, instance, NULL);
|
| 78421 |
kalibera |
226 |
|
|
|
227 |
process_request_mutex = CreateMutex(NULL, FALSE, NULL);
|
|
|
228 |
if (!process_request_mutex)
|
|
|
229 |
DBG(printf("Mutex creation failed\n"));
|
| 48990 |
urbaneks |
230 |
#endif
|
|
|
231 |
needs_init = 0;
|
|
|
232 |
}
|
|
|
233 |
|
| 54055 |
urbaneks |
234 |
/* free buffers starting from the tail(!!) */
|
|
|
235 |
static void free_buffer(struct buffer *buf) {
|
|
|
236 |
if (!buf) return;
|
|
|
237 |
if (buf->prev) free_buffer(buf->prev);
|
|
|
238 |
free(buf);
|
|
|
239 |
}
|
|
|
240 |
|
|
|
241 |
/* allocate a new buffer */
|
|
|
242 |
static struct buffer *alloc_buffer(int size, struct buffer *parent) {
|
|
|
243 |
struct buffer *buf = (struct buffer*) malloc(sizeof(struct buffer) + size);
|
|
|
244 |
if (!buf) return buf;
|
|
|
245 |
buf->next = 0;
|
|
|
246 |
buf->prev = parent;
|
|
|
247 |
if (parent) parent->next = buf;
|
|
|
248 |
buf->size = size;
|
|
|
249 |
buf->length = 0;
|
|
|
250 |
return buf;
|
|
|
251 |
}
|
|
|
252 |
|
|
|
253 |
/* convert doubly-linked buffers into one big raw vector */
|
|
|
254 |
static SEXP collect_buffers(struct buffer *buf) {
|
|
|
255 |
SEXP res;
|
|
|
256 |
char *dst;
|
|
|
257 |
int len = 0;
|
|
|
258 |
if (!buf) return allocVector(RAWSXP, 0);
|
|
|
259 |
while (buf->prev) { /* count the total length and find the root */
|
|
|
260 |
len += buf->length;
|
|
|
261 |
buf = buf->prev;
|
|
|
262 |
}
|
|
|
263 |
res = allocVector(RAWSXP, len + buf->length);
|
|
|
264 |
dst = (char*) RAW(res);
|
|
|
265 |
while (buf) {
|
| 87180 |
kalibera |
266 |
if (buf->length)
|
|
|
267 |
memcpy(dst, buf->data, buf->length);
|
| 54055 |
urbaneks |
268 |
dst += buf->length;
|
|
|
269 |
buf = buf->next;
|
|
|
270 |
}
|
|
|
271 |
return res;
|
|
|
272 |
}
|
|
|
273 |
|
| 49434 |
ripley |
274 |
static void finalize_worker(httpd_conn_t *c)
|
|
|
275 |
{
|
| 48990 |
urbaneks |
276 |
DBG(printf("finalizing worker %p\n", (void*) c));
|
| 65805 |
ripley |
277 |
#ifndef _WIN32
|
| 48990 |
urbaneks |
278 |
if (c->ih) {
|
|
|
279 |
removeInputHandler(&R_InputHandlers, c->ih);
|
|
|
280 |
c->ih = NULL;
|
|
|
281 |
}
|
|
|
282 |
#endif
|
|
|
283 |
if (c->url) {
|
|
|
284 |
free(c->url);
|
|
|
285 |
c->url = NULL;
|
|
|
286 |
}
|
| 49434 |
ripley |
287 |
|
| 48990 |
urbaneks |
288 |
if (c->body) {
|
|
|
289 |
free(c->body);
|
|
|
290 |
c->body = NULL;
|
|
|
291 |
}
|
| 49434 |
ripley |
292 |
|
| 51562 |
urbaneks |
293 |
if (c->content_type) {
|
|
|
294 |
free(c->content_type);
|
|
|
295 |
c->content_type = NULL;
|
|
|
296 |
}
|
| 54055 |
urbaneks |
297 |
if (c->headers) {
|
|
|
298 |
free_buffer(c->headers);
|
|
|
299 |
c->headers = NULL;
|
|
|
300 |
}
|
| 48990 |
urbaneks |
301 |
if (c->sock != INVALID_SOCKET) {
|
|
|
302 |
closesocket(c->sock);
|
|
|
303 |
c->sock = INVALID_SOCKET;
|
|
|
304 |
}
|
|
|
305 |
}
|
|
|
306 |
|
| 49434 |
ripley |
307 |
/* adds a worker to the worker list and returns 0. If the list is
|
|
|
308 |
* full, the worker is finalized and returns -1.
|
|
|
309 |
* Note that we don't need locking, because add_worker is guaranteed
|
|
|
310 |
* to be called by the same thread (server thread).
|
|
|
311 |
*/
|
| 48990 |
urbaneks |
312 |
static int add_worker(httpd_conn_t *c) {
|
|
|
313 |
unsigned int i = 0;
|
|
|
314 |
for (; i < MAX_WORKERS; i++)
|
|
|
315 |
if (!workers[i]) {
|
| 65805 |
ripley |
316 |
#ifdef _WIN32
|
| 86510 |
kalibera |
317 |
DBG(printf("registering worker %p as %d (thread=%p)\n", (void*) c, i, (void *) c->thread));
|
| 48990 |
urbaneks |
318 |
#else
|
|
|
319 |
DBG(printf("registering worker %p as %d (handler=%p)\n", (void*) c, i, (void*) c->ih));
|
|
|
320 |
#endif
|
|
|
321 |
workers[i] = c;
|
|
|
322 |
return 0;
|
|
|
323 |
}
|
| 49434 |
ripley |
324 |
/* FIXME: ok no more space for a new worker - what do we do now?
|
|
|
325 |
* for now we just drop it on the floor .. */
|
| 48990 |
urbaneks |
326 |
finalize_worker(c);
|
|
|
327 |
free(c);
|
|
|
328 |
return -1;
|
|
|
329 |
}
|
|
|
330 |
|
| 49434 |
ripley |
331 |
/* finalize worker, remove it from the list and free the memory. If
|
|
|
332 |
* the worker is owned by a thread, it is not finalized and the
|
|
|
333 |
* THREAD_DISPOSE flag is set instead. */
|
|
|
334 |
static void remove_worker(httpd_conn_t *c)
|
|
|
335 |
{
|
| 48990 |
urbaneks |
336 |
unsigned int i = 0;
|
|
|
337 |
if (!c) return;
|
| 49434 |
ripley |
338 |
if (c->attr & THREAD_OWNED) { /* if the worker is used by a
|
|
|
339 |
* thread, we can only signal for
|
|
|
340 |
* its removal */
|
| 48990 |
urbaneks |
341 |
c->attr |= THREAD_DISPOSE;
|
|
|
342 |
return;
|
|
|
343 |
}
|
|
|
344 |
finalize_worker(c);
|
|
|
345 |
for (; i < MAX_WORKERS; i++)
|
|
|
346 |
if (workers[i] == c)
|
|
|
347 |
workers[i] = NULL;
|
|
|
348 |
DBG(printf("removing worker %p\n", (void*) c));
|
| 49434 |
ripley |
349 |
free(c);
|
| 48990 |
urbaneks |
350 |
}
|
|
|
351 |
|
| 51805 |
urbaneks |
352 |
#ifndef Win32
|
|
|
353 |
extern int R_ignore_SIGPIPE; /* defined in src/main/main.c on unix */
|
|
|
354 |
#else
|
|
|
355 |
static int R_ignore_SIGPIPE; /* for simplicity of the code below */
|
|
|
356 |
#endif
|
|
|
357 |
|
| 59184 |
ripley |
358 |
static int send_response(SOCKET s, const char *buf, size_t len)
|
| 49434 |
ripley |
359 |
{
|
| 48990 |
urbaneks |
360 |
unsigned int i = 0;
|
| 51805 |
urbaneks |
361 |
/* we have to tell R to ignore SIGPIPE otherwise it can raise an error
|
|
|
362 |
and get us into deep trouble */
|
|
|
363 |
R_ignore_SIGPIPE = 1;
|
| 48990 |
urbaneks |
364 |
while (i < len) {
|
| 59184 |
ripley |
365 |
ssize_t n = send(s, buf + i, len - i, 0);
|
| 51805 |
urbaneks |
366 |
if (n < 1) {
|
|
|
367 |
R_ignore_SIGPIPE = 0;
|
|
|
368 |
return -1;
|
|
|
369 |
}
|
| 48990 |
urbaneks |
370 |
i += n;
|
|
|
371 |
}
|
| 51805 |
urbaneks |
372 |
R_ignore_SIGPIPE = 0;
|
| 48990 |
urbaneks |
373 |
return 0;
|
|
|
374 |
}
|
|
|
375 |
|
| 49887 |
urbaneks |
376 |
/* sends HTTP/x.x plus the text (which should be of the form " XXX ...") */
|
|
|
377 |
static int send_http_response(httpd_conn_t *c, const char *text) {
|
| 51562 |
urbaneks |
378 |
char buf[96];
|
| 49887 |
urbaneks |
379 |
const char *s = HTTP_SIG(c);
|
| 59184 |
ripley |
380 |
size_t l = strlen(text);
|
|
|
381 |
ssize_t res;
|
| 51562 |
urbaneks |
382 |
/* reduce the number of packets by sending the payload en-block from buf */
|
|
|
383 |
if (l < sizeof(buf) - 10) {
|
|
|
384 |
strcpy(buf, s);
|
|
|
385 |
strcpy(buf + 8, text);
|
|
|
386 |
return send_response(c->sock, buf, l + 8);
|
|
|
387 |
}
|
| 51805 |
urbaneks |
388 |
R_ignore_SIGPIPE = 1;
|
|
|
389 |
res = send(c->sock, s, 8, 0);
|
|
|
390 |
R_ignore_SIGPIPE = 0;
|
|
|
391 |
if (res < 8) return -1;
|
| 49887 |
urbaneks |
392 |
return send_response(c->sock, text, strlen(text));
|
|
|
393 |
}
|
|
|
394 |
|
| 48990 |
urbaneks |
395 |
/* decode URI in place (decoding never expands) */
|
| 49434 |
ripley |
396 |
static void uri_decode(char *s)
|
|
|
397 |
{
|
| 48990 |
urbaneks |
398 |
char *t = s;
|
|
|
399 |
while (*s) {
|
|
|
400 |
if (*s == '+') { /* + -> SPC */
|
|
|
401 |
*(t++) = ' '; s++;
|
|
|
402 |
} else if (*s == '%') {
|
|
|
403 |
unsigned char ec = 0;
|
|
|
404 |
s++;
|
|
|
405 |
if (*s >= '0' && *s <= '9') ec |= ((unsigned char)(*s - '0')) << 4;
|
|
|
406 |
else if (*s >= 'a' && *s <= 'f') ec |= ((unsigned char)(*s - 'a' + 10)) << 4;
|
|
|
407 |
else if (*s >= 'A' && *s <= 'F') ec |= ((unsigned char)(*s - 'A' + 10)) << 4;
|
|
|
408 |
if (*s) s++;
|
|
|
409 |
if (*s >= '0' && *s <= '9') ec |= (unsigned char)(*s - '0');
|
|
|
410 |
else if (*s >= 'a' && *s <= 'f') ec |= (unsigned char)(*s - 'a' + 10);
|
|
|
411 |
else if (*s >= 'A' && *s <= 'F') ec |= (unsigned char)(*s - 'A' + 10);
|
|
|
412 |
if (*s) s++;
|
|
|
413 |
*(t++) = (char) ec;
|
|
|
414 |
} else *(t++) = *(s++);
|
|
|
415 |
}
|
|
|
416 |
*t = 0;
|
|
|
417 |
}
|
|
|
418 |
|
| 49434 |
ripley |
419 |
/* parse a query string into a named character vector - must NOT be
|
|
|
420 |
* URI decoded */
|
|
|
421 |
static SEXP parse_query(char *query)
|
|
|
422 |
{
|
| 48990 |
urbaneks |
423 |
int parts = 0;
|
|
|
424 |
SEXP res, names;
|
|
|
425 |
char *s = query, *key = 0, *value = query, *t = query;
|
|
|
426 |
while (*s) {
|
|
|
427 |
if (*s == '&') parts++;
|
|
|
428 |
s++;
|
|
|
429 |
}
|
|
|
430 |
parts++;
|
|
|
431 |
res = PROTECT(allocVector(STRSXP, parts));
|
|
|
432 |
names = PROTECT(allocVector(STRSXP, parts));
|
|
|
433 |
s = query;
|
|
|
434 |
parts = 0;
|
|
|
435 |
while (1) {
|
|
|
436 |
if (*s == '=' && !key) { /* first '=' in a part */
|
|
|
437 |
key = value;
|
|
|
438 |
*(t++) = 0;
|
|
|
439 |
value = t;
|
|
|
440 |
s++;
|
|
|
441 |
} else if (*s == '&' || !*s) { /* next part */
|
|
|
442 |
int last_entry = !*s;
|
|
|
443 |
*(t++) = 0;
|
|
|
444 |
if (!key) key = "";
|
|
|
445 |
SET_STRING_ELT(names, parts, mkChar(key));
|
|
|
446 |
SET_STRING_ELT(res, parts, mkChar(value));
|
|
|
447 |
parts++;
|
|
|
448 |
if (last_entry) break;
|
|
|
449 |
key = 0;
|
|
|
450 |
value = t;
|
|
|
451 |
s++;
|
|
|
452 |
} else if (*s == '+') { /* + -> SPC */
|
|
|
453 |
*(t++) = ' '; s++;
|
|
|
454 |
} else if (*s == '%') { /* we cannot use uri_decode becasue we need &/= *before* decoding */
|
|
|
455 |
unsigned char ec = 0;
|
|
|
456 |
s++;
|
|
|
457 |
if (*s >= '0' && *s <= '9') ec |= ((unsigned char)(*s - '0')) << 4;
|
|
|
458 |
else if (*s >= 'a' && *s <= 'f') ec |= ((unsigned char)(*s - 'a' + 10)) << 4;
|
|
|
459 |
else if (*s >= 'A' && *s <= 'F') ec |= ((unsigned char)(*s - 'A' + 10)) << 4;
|
|
|
460 |
if (*s) s++;
|
|
|
461 |
if (*s >= '0' && *s <= '9') ec |= (unsigned char)(*s - '0');
|
|
|
462 |
else if (*s >= 'a' && *s <= 'f') ec |= (unsigned char)(*s - 'a' + 10);
|
|
|
463 |
else if (*s >= 'A' && *s <= 'F') ec |= (unsigned char)(*s - 'A' + 10);
|
|
|
464 |
if (*s) s++;
|
|
|
465 |
*(t++) = (char) ec;
|
|
|
466 |
} else *(t++) = *(s++);
|
|
|
467 |
}
|
|
|
468 |
setAttrib(res, R_NamesSymbol, names);
|
|
|
469 |
UNPROTECT(2);
|
|
|
470 |
return res;
|
|
|
471 |
}
|
|
|
472 |
|
| 54055 |
urbaneks |
473 |
static SEXP R_ContentTypeName, R_HandlersName;
|
|
|
474 |
|
| 51562 |
urbaneks |
475 |
/* create an object representing the request body. It is NULL if the body is empty (or zero length).
|
|
|
476 |
* In the case of a URL encoded form it will have the same shape as the query string (named string vector).
|
|
|
477 |
* In all other cases it will be a raw vector with a "content-type" attribute (if specified in the headers) */
|
|
|
478 |
static SEXP parse_request_body(httpd_conn_t *c) {
|
|
|
479 |
if (!c || !c->body) return R_NilValue;
|
|
|
480 |
|
|
|
481 |
if (c->attr & CONTENT_FORM_UENC) { /* URL encoded form - return parsed form */
|
|
|
482 |
c->body[c->content_length] = 0; /* the body is guaranteed to have an extra byte for the termination */
|
|
|
483 |
return parse_query(c->body);
|
|
|
484 |
} else { /* something else - pass it as a raw vector */
|
|
|
485 |
SEXP res = PROTECT(Rf_allocVector(RAWSXP, c->content_length));
|
|
|
486 |
if (c->content_length)
|
|
|
487 |
memcpy(RAW(res), c->body, c->content_length);
|
| 54055 |
urbaneks |
488 |
if (c->content_type) { /* attach the content type so it can be interpreted */
|
|
|
489 |
if (!R_ContentTypeName) R_ContentTypeName = install("content-type");
|
|
|
490 |
setAttrib(res, R_ContentTypeName, mkString(c->content_type));
|
|
|
491 |
}
|
| 51562 |
urbaneks |
492 |
UNPROTECT(1);
|
|
|
493 |
return res;
|
|
|
494 |
}
|
|
|
495 |
}
|
|
|
496 |
|
| 65805 |
ripley |
497 |
#ifdef _WIN32
|
| 49434 |
ripley |
498 |
/* on Windows we have to guarantee that process_request is performed
|
|
|
499 |
* on the main thread, so we have to dispatch it through a message */
|
| 48990 |
urbaneks |
500 |
static void process_request_main_thread(httpd_conn_t *c);
|
|
|
501 |
|
| 49434 |
ripley |
502 |
static void process_request(httpd_conn_t *c)
|
|
|
503 |
{
|
| 78421 |
kalibera |
504 |
if (WaitForSingleObject(process_request_mutex, INFINITE) != 0) {
|
|
|
505 |
DBG(printf("Acquiring mutex failed\n"));
|
|
|
506 |
/* (very) unexpected error, maybe we're shutting down? */
|
|
|
507 |
return;
|
|
|
508 |
}
|
|
|
509 |
|
| 49434 |
ripley |
510 |
/* SendMessage is synchronous, so it will wait until the message
|
|
|
511 |
* is processed */
|
| 86511 |
kalibera |
512 |
DBG(printf("enqueuing process_request_main_thread\n"));
|
| 48990 |
urbaneks |
513 |
SendMessage(message_window, WM_RHTTP_CALLBACK, 0, (LPARAM) c);
|
| 86511 |
kalibera |
514 |
DBG(printf("process_request_main_thread returned\n"));
|
| 78421 |
kalibera |
515 |
|
|
|
516 |
ReleaseMutex(process_request_mutex);
|
| 48990 |
urbaneks |
517 |
}
|
| 49434 |
ripley |
518 |
#define process_request process_request_main_thread
|
| 48990 |
urbaneks |
519 |
#endif
|
|
|
520 |
|
| 49887 |
urbaneks |
521 |
/* finalize a request - essentially for HTTP/1.0 it means that
|
|
|
522 |
* we have to close the connection */
|
|
|
523 |
static void fin_request(httpd_conn_t *c) {
|
|
|
524 |
if (!IS_HTTP_1_1(c))
|
|
|
525 |
c->attr |= CONNECTION_CLOSE;
|
|
|
526 |
}
|
|
|
527 |
|
| 51563 |
urbaneks |
528 |
static SEXP custom_handlers_env;
|
|
|
529 |
|
|
|
530 |
/* returns a httpd handler (closure) for a given path. As a special case
|
|
|
531 |
* it can return a symbol that will be resolved in the "tools" namespace.
|
|
|
532 |
* currently it allows custom handlers for paths of the form
|
|
|
533 |
* /custom/<name>[/.*] where <name> must less than 64 characters long
|
|
|
534 |
* and is matched against closures in tools:::.httpd.handlers.env */
|
|
|
535 |
static SEXP handler_for_path(const char *path) {
|
|
|
536 |
if (path && !strncmp(path, "/custom/", 8)) { /* starts with /custom/ ? */
|
|
|
537 |
const char *c = path + 8, *e = c;
|
|
|
538 |
while (*c && *c != '/') c++; /* find out the name */
|
|
|
539 |
if (c - e > 0 && c - e < 64) { /* if it's 1..63 chars long, proceed */
|
|
|
540 |
char fn[64];
|
|
|
541 |
memcpy(fn, e, c - e); /* create a local C string with the name for the install() call */
|
|
|
542 |
fn[c - e] = 0;
|
| 86511 |
kalibera |
543 |
DBG(printf("handler_for_path('%s'): looking up custom handler '%s'\n", path, fn));
|
| 51563 |
urbaneks |
544 |
/* we cache custom_handlers_env so in case it has not been loaded yet, fetch it */
|
| 54055 |
urbaneks |
545 |
if (!custom_handlers_env) {
|
|
|
546 |
if (!R_HandlersName) R_HandlersName = install(".httpd.handlers.env");
|
| 68392 |
luke |
547 |
SEXP toolsNS = PROTECT(R_FindNamespace(mkString("tools")));
|
|
|
548 |
custom_handlers_env = eval(R_HandlersName, toolsNS);
|
|
|
549 |
UNPROTECT(1); /* toolsNS */
|
| 54055 |
urbaneks |
550 |
}
|
| 51563 |
urbaneks |
551 |
/* we only proceed if .httpd.handlers.env really exists */
|
|
|
552 |
if (TYPEOF(custom_handlers_env) == ENVSXP) {
|
| 86790 |
luke |
553 |
SEXP cl = findVarInFrame(custom_handlers_env, install(fn));
|
| 51563 |
urbaneks |
554 |
if (cl != R_UnboundValue && TYPEOF(cl) == CLOSXP) /* we need a closure */
|
|
|
555 |
return cl;
|
|
|
556 |
}
|
|
|
557 |
}
|
|
|
558 |
}
|
| 86511 |
kalibera |
559 |
DBG(printf(" - falling back to default httpd\n"));
|
| 51563 |
urbaneks |
560 |
return install("httpd");
|
|
|
561 |
}
|
|
|
562 |
|
| 48990 |
urbaneks |
563 |
/* process a request by calling the httpd() function in R */
|
| 59338 |
urbaneks |
564 |
static void process_request_(void *ptr)
|
| 49434 |
ripley |
565 |
{
|
| 59338 |
urbaneks |
566 |
httpd_conn_t *c = (httpd_conn_t*) ptr;
|
| 48990 |
urbaneks |
567 |
const char *ct = "text/html";
|
|
|
568 |
char *query = 0, *s;
|
|
|
569 |
SEXP sHeaders = R_NilValue;
|
|
|
570 |
int code = 200;
|
| 82334 |
kalibera |
571 |
const void *vmax = NULL;
|
|
|
572 |
|
| 86511 |
kalibera |
573 |
DBG(printf("process request for %p\n", (void*) c));
|
| 48990 |
urbaneks |
574 |
if (!c || !c->url) return; /* if there is not enough to process, bail out */
|
| 82334 |
kalibera |
575 |
vmax = vmaxget();
|
| 48990 |
urbaneks |
576 |
s = c->url;
|
|
|
577 |
while (*s && *s != '?') s++; /* find the query part */
|
|
|
578 |
if (*s) {
|
|
|
579 |
*(s++) = 0;
|
|
|
580 |
query = s;
|
|
|
581 |
}
|
|
|
582 |
uri_decode(c->url); /* decode the path part */
|
| 51562 |
urbaneks |
583 |
{ /* construct "try(httpd(url, query, body), silent=TRUE)" */
|
| 48990 |
urbaneks |
584 |
SEXP sTrue = PROTECT(ScalarLogical(TRUE));
|
| 54055 |
urbaneks |
585 |
SEXP sBody = PROTECT(parse_request_body(c));
|
|
|
586 |
SEXP sQuery = PROTECT(query ? parse_query(query) : R_NilValue);
|
|
|
587 |
SEXP sReqHeaders = PROTECT(c->headers ? collect_buffers(c->headers) : R_NilValue);
|
|
|
588 |
SEXP sArgs = PROTECT(list4(mkString(c->url), sQuery, sBody, sReqHeaders));
|
|
|
589 |
SEXP sTry = install("try");
|
|
|
590 |
SEXP y, x = PROTECT(lang3(sTry,
|
|
|
591 |
LCONS(handler_for_path(c->url), sArgs),
|
| 51562 |
urbaneks |
592 |
sTrue));
|
| 48990 |
urbaneks |
593 |
SET_TAG(CDR(CDR(x)), install("silent"));
|
| 86511 |
kalibera |
594 |
DBG(printf("eval(try(httpd('%s'),silent=TRUE))\n", c->url));
|
| 54055 |
urbaneks |
595 |
|
| 51562 |
urbaneks |
596 |
/* evaluate the above in the tools namespace */
|
| 68392 |
luke |
597 |
SEXP toolsNS = PROTECT(R_FindNamespace(mkString("tools")));
|
|
|
598 |
x = eval(x, toolsNS);
|
|
|
599 |
UNPROTECT(1); /* toolsNS */
|
|
|
600 |
PROTECT(x);
|
| 48990 |
urbaneks |
601 |
|
|
|
602 |
/* the result is expected to have one of the following forms:
|
|
|
603 |
|
| 82506 |
kalibera |
604 |
a) character vector of length 1 => error (possibly from try),
|
|
|
605 |
will create 500 response
|
| 49434 |
ripley |
606 |
|
| 82506 |
kalibera |
607 |
the string must specify charset UTF-8 and the server will
|
|
|
608 |
convert it to UTF-8 (must be in sync with dynamicHelp.R)
|
|
|
609 |
|
|
|
610 |
|
| 49434 |
ripley |
611 |
b) list(payload[, content-type[, headers[, status code]]])
|
|
|
612 |
|
|
|
613 |
payload: can be a character vector of length one or a
|
|
|
614 |
raw vector. if the character vector is named "file" then
|
| 82506 |
kalibera |
615 |
the content of a file of that name is the payload (the file
|
|
|
616 |
will be served byte-by-byte and the headers must specify
|
|
|
617 |
the charset of the file). if the character vector is not named
|
|
|
618 |
"file", it will be converted to UTF-8 (and hence the vector
|
|
|
619 |
itself must specify charset UTF-8, must be in sync with
|
|
|
620 |
dynamicHelp.R)
|
| 49434 |
ripley |
621 |
|
|
|
622 |
content-type: must be a character vector of length one
|
|
|
623 |
or NULL (if present, else default is "text/html")
|
|
|
624 |
|
|
|
625 |
headers: must be a character vector - the elements will
|
|
|
626 |
have CRLF appended and neither Content-type nor
|
|
|
627 |
Content-length may be used
|
|
|
628 |
|
|
|
629 |
status code: must be an integer if present (default is 200)
|
|
|
630 |
*/
|
|
|
631 |
|
| 48990 |
urbaneks |
632 |
if (TYPEOF(x) == STRSXP && LENGTH(x) > 0) { /* string means there was an error */
|
| 82506 |
kalibera |
633 |
const char *s = translateCharUTF8(STRING_ELT(x, 0));
|
| 49887 |
urbaneks |
634 |
send_http_response(c, " 500 Evaluation error\r\nConnection: close\r\nContent-type: text/plain\r\n\r\n");
|
| 86511 |
kalibera |
635 |
DBG(printf("respond with 500 and content: %s\n", translateChar(STRING_ELT(x, 0))));
|
| 48990 |
urbaneks |
636 |
if (c->method != METHOD_HEAD)
|
|
|
637 |
send_response(c->sock, s, strlen(s));
|
|
|
638 |
c->attr |= CONNECTION_CLOSE; /* force close */
|
| 54055 |
urbaneks |
639 |
UNPROTECT(7);
|
| 82334 |
kalibera |
640 |
vmaxset(vmax);
|
| 48990 |
urbaneks |
641 |
return;
|
|
|
642 |
}
|
| 48995 |
urbaneks |
643 |
|
|
|
644 |
if (TYPEOF(x) == VECSXP && LENGTH(x) > 0) { /* a list (generic vector) can be a real payload */
|
|
|
645 |
SEXP xNames = getAttrib(x, R_NamesSymbol);
|
| 48990 |
urbaneks |
646 |
if (LENGTH(x) > 1) {
|
| 48995 |
urbaneks |
647 |
SEXP sCT = VECTOR_ELT(x, 1); /* second element is content type if present */
|
| 48990 |
urbaneks |
648 |
if (TYPEOF(sCT) == STRSXP && LENGTH(sCT) > 0)
|
| 82506 |
kalibera |
649 |
ct = translateCharUTF8(STRING_ELT(sCT, 0));
|
| 48995 |
urbaneks |
650 |
if (LENGTH(x) > 2) { /* third element is headers vector */
|
| 48990 |
urbaneks |
651 |
sHeaders = VECTOR_ELT(x, 2);
|
|
|
652 |
if (TYPEOF(sHeaders) != STRSXP)
|
|
|
653 |
sHeaders = R_NilValue;
|
| 48995 |
urbaneks |
654 |
if (LENGTH(x) > 3) /* fourth element is HTTP code */
|
| 48990 |
urbaneks |
655 |
code = asInteger(VECTOR_ELT(x, 3));
|
|
|
656 |
}
|
|
|
657 |
}
|
|
|
658 |
y = VECTOR_ELT(x, 0);
|
|
|
659 |
if (TYPEOF(y) == STRSXP && LENGTH(y) > 0) {
|
|
|
660 |
char buf[64];
|
| 82506 |
kalibera |
661 |
const char *cs = translateCharUTF8(STRING_ELT(y, 0)), *fn = 0;
|
| 48990 |
urbaneks |
662 |
if (code == 200)
|
| 49887 |
urbaneks |
663 |
send_http_response(c, " 200 OK\r\nContent-type: ");
|
| 48990 |
urbaneks |
664 |
else {
|
| 83436 |
ripley |
665 |
snprintf(buf, 64, "%s %d Code %d\r\nContent-type: ",
|
|
|
666 |
HTTP_SIG(c), code, code);
|
| 48990 |
urbaneks |
667 |
send_response(c->sock, buf, strlen(buf));
|
|
|
668 |
}
|
|
|
669 |
send_response(c->sock, ct, strlen(ct));
|
|
|
670 |
if (sHeaders != R_NilValue) {
|
|
|
671 |
unsigned int i = 0, n = LENGTH(sHeaders);
|
|
|
672 |
for (; i < n; i++) {
|
| 82506 |
kalibera |
673 |
const char *hs = translateCharUTF8(STRING_ELT(sHeaders, i));
|
| 48990 |
urbaneks |
674 |
send_response(c->sock, "\r\n", 2);
|
|
|
675 |
send_response(c->sock, hs, strlen(hs));
|
|
|
676 |
}
|
|
|
677 |
}
|
| 48995 |
urbaneks |
678 |
/* special content - a file: either list(file="") or list(c("*FILE*", "")) - the latter will go away */
|
|
|
679 |
if (TYPEOF(xNames) == STRSXP && LENGTH(xNames) > 0 &&
|
| 82334 |
kalibera |
680 |
!strcmp(translateChar(STRING_ELT(xNames, 0)), "file"))
|
| 82506 |
kalibera |
681 |
fn = translateChar(STRING_ELT(y, 0)); /* translateCharFP2 not exported */
|
| 48995 |
urbaneks |
682 |
if (LENGTH(y) > 1 && !strcmp(cs, "*FILE*"))
|
| 82336 |
kalibera |
683 |
fn = translateChar(STRING_ELT(y, 1)); /* translateCharFP2 not exported */
|
| 48995 |
urbaneks |
684 |
if (fn) {
|
| 48990 |
urbaneks |
685 |
char *fbuf;
|
|
|
686 |
FILE *f = fopen(fn, "rb");
|
|
|
687 |
long fsz = 0;
|
|
|
688 |
if (!f) {
|
|
|
689 |
send_response(c->sock, "\r\nContent-length: 0\r\n\r\n", 23);
|
| 54055 |
urbaneks |
690 |
UNPROTECT(7);
|
| 49887 |
urbaneks |
691 |
fin_request(c);
|
| 82334 |
kalibera |
692 |
vmaxset(vmax);
|
| 48990 |
urbaneks |
693 |
return;
|
|
|
694 |
}
|
|
|
695 |
fseek(f, 0, SEEK_END);
|
|
|
696 |
fsz = ftell(f);
|
|
|
697 |
fseek(f, 0, SEEK_SET);
|
| 83436 |
ripley |
698 |
snprintf(buf, 64, "\r\nContent-length: %ld\r\n\r\n", fsz);
|
| 48993 |
urbaneks |
699 |
send_response(c->sock, buf, strlen(buf));
|
| 48990 |
urbaneks |
700 |
if (c->method != METHOD_HEAD) {
|
|
|
701 |
fbuf = (char*) malloc(32768);
|
| 51562 |
urbaneks |
702 |
if (fbuf) {
|
|
|
703 |
while (fsz > 0 && !feof(f)) {
|
| 59184 |
ripley |
704 |
size_t rd = (fsz > 32768) ? 32768 : fsz;
|
| 51562 |
urbaneks |
705 |
if (fread(fbuf, 1, rd, f) != rd) {
|
|
|
706 |
free(fbuf);
|
| 54055 |
urbaneks |
707 |
UNPROTECT(7);
|
| 51562 |
urbaneks |
708 |
c->attr |= CONNECTION_CLOSE;
|
| 69259 |
luke |
709 |
fclose(f);
|
| 82334 |
kalibera |
710 |
vmaxset(vmax);
|
| 51562 |
urbaneks |
711 |
return;
|
|
|
712 |
}
|
|
|
713 |
send_response(c->sock, fbuf, rd);
|
|
|
714 |
fsz -= rd;
|
| 48990 |
urbaneks |
715 |
}
|
| 51562 |
urbaneks |
716 |
free(fbuf);
|
|
|
717 |
} else { /* allocation error - get out */
|
| 54055 |
urbaneks |
718 |
UNPROTECT(7);
|
| 51562 |
urbaneks |
719 |
c->attr |= CONNECTION_CLOSE;
|
| 69259 |
luke |
720 |
fclose(f);
|
| 82334 |
kalibera |
721 |
vmaxset(vmax);
|
| 51562 |
urbaneks |
722 |
return;
|
| 48990 |
urbaneks |
723 |
}
|
|
|
724 |
}
|
|
|
725 |
fclose(f);
|
| 54055 |
urbaneks |
726 |
UNPROTECT(7);
|
| 49887 |
urbaneks |
727 |
fin_request(c);
|
| 82334 |
kalibera |
728 |
vmaxset(vmax);
|
| 48990 |
urbaneks |
729 |
return;
|
|
|
730 |
}
|
| 83436 |
ripley |
731 |
snprintf(buf, 64, "\r\nContent-length: %u\r\n\r\n",
|
|
|
732 |
(unsigned int) strlen(cs));
|
| 48990 |
urbaneks |
733 |
send_response(c->sock, buf, strlen(buf));
|
|
|
734 |
if (c->method != METHOD_HEAD)
|
|
|
735 |
send_response(c->sock, cs, strlen(cs));
|
| 54055 |
urbaneks |
736 |
UNPROTECT(7);
|
| 49887 |
urbaneks |
737 |
fin_request(c);
|
| 82334 |
kalibera |
738 |
vmaxset(vmax);
|
| 48990 |
urbaneks |
739 |
return;
|
|
|
740 |
}
|
|
|
741 |
if (TYPEOF(y) == RAWSXP) {
|
|
|
742 |
char buf[64];
|
|
|
743 |
Rbyte *cs = RAW(y);
|
|
|
744 |
if (code == 200)
|
| 49887 |
urbaneks |
745 |
send_http_response(c, " 200 OK\r\nContent-type: ");
|
| 48990 |
urbaneks |
746 |
else {
|
| 83436 |
ripley |
747 |
snprintf(buf, 64, "%s %d Code %d\r\nContent-type: ",
|
|
|
748 |
HTTP_SIG(c), code, code);
|
| 48990 |
urbaneks |
749 |
send_response(c->sock, buf, strlen(buf));
|
|
|
750 |
}
|
|
|
751 |
send_response(c->sock, ct, strlen(ct));
|
|
|
752 |
if (sHeaders != R_NilValue) {
|
|
|
753 |
unsigned int i = 0, n = LENGTH(sHeaders);
|
|
|
754 |
for (; i < n; i++) {
|
| 82506 |
kalibera |
755 |
const char *hs = translateCharUTF8(STRING_ELT(sHeaders, i));
|
| 48990 |
urbaneks |
756 |
send_response(c->sock, "\r\n", 2);
|
|
|
757 |
send_response(c->sock, hs, strlen(hs));
|
|
|
758 |
}
|
|
|
759 |
}
|
| 83436 |
ripley |
760 |
snprintf(buf, 64, "\r\nContent-length: %u\r\n\r\n", LENGTH(y));
|
| 48990 |
urbaneks |
761 |
send_response(c->sock, buf, strlen(buf));
|
|
|
762 |
if (c->method != METHOD_HEAD)
|
|
|
763 |
send_response(c->sock, (char*) cs, LENGTH(y));
|
| 54055 |
urbaneks |
764 |
UNPROTECT(7);
|
| 49887 |
urbaneks |
765 |
fin_request(c);
|
| 82334 |
kalibera |
766 |
vmaxset(vmax);
|
| 48990 |
urbaneks |
767 |
return;
|
|
|
768 |
}
|
|
|
769 |
}
|
| 54055 |
urbaneks |
770 |
UNPROTECT(7);
|
| 48990 |
urbaneks |
771 |
}
|
| 49887 |
urbaneks |
772 |
send_http_response(c, " 500 Invalid response from R\r\nConnection: close\r\nContent-type: text/plain\r\n\r\nServer error: invalid response from R\r\n");
|
| 48990 |
urbaneks |
773 |
c->attr |= CONNECTION_CLOSE; /* force close */
|
| 82334 |
kalibera |
774 |
vmaxset(vmax);
|
| 48990 |
urbaneks |
775 |
}
|
|
|
776 |
|
| 59338 |
urbaneks |
777 |
/* wrap the actual call with ToplevelExec since we need to have a guaranteed
|
|
|
778 |
return so we can track the presence of a worker code inside R to prevent
|
| 78421 |
kalibera |
779 |
re-entrance from other clients
|
|
|
780 |
|
|
|
781 |
on Windows, this function is named process_request_main_thread via
|
|
|
782 |
C preprocessor; on all platforms it is executed on the main R thread
|
|
|
783 |
*/
|
| 59338 |
urbaneks |
784 |
static void process_request(httpd_conn_t *c)
|
|
|
785 |
{
|
| 78421 |
kalibera |
786 |
#ifndef _WIN32
|
| 59338 |
urbaneks |
787 |
in_process = 1;
|
| 78421 |
kalibera |
788 |
#endif
|
| 59338 |
urbaneks |
789 |
R_ToplevelExec(process_request_, c);
|
| 78421 |
kalibera |
790 |
#ifndef _WIN32
|
| 59338 |
urbaneks |
791 |
in_process = 0;
|
| 78421 |
kalibera |
792 |
#endif
|
| 59338 |
urbaneks |
793 |
}
|
|
|
794 |
|
| 65805 |
ripley |
795 |
#ifdef _WIN32
|
| 48990 |
urbaneks |
796 |
#undef process_request
|
|
|
797 |
#endif
|
|
|
798 |
|
| 79433 |
kalibera |
799 |
/* Remove . and (most) .. from "p" following RFC 3986, 5.2.4.*/
|
| 79429 |
kalibera |
800 |
static char *remove_dot_segments(char *p) {
|
|
|
801 |
|
|
|
802 |
char *inbuf = Rstrdup(p);
|
|
|
803 |
char *in = inbuf; /* first byte of input buffer */
|
|
|
804 |
|
| 79433 |
kalibera |
805 |
char *outbuf = malloc(strlen(inbuf) + 1);
|
|
|
806 |
if (!outbuf)
|
|
|
807 |
error("allocation error in remove_dot_segments");
|
| 79429 |
kalibera |
808 |
char *out = outbuf; /* last byte (terminator) of output buffer */
|
|
|
809 |
*out = '\0';
|
|
|
810 |
|
|
|
811 |
while(*in) {
|
|
|
812 |
/*
|
|
|
813 |
A. If the input buffer begins with a prefix of "../" or "./",
|
|
|
814 |
then remove that prefix from the input buffer; otherwise,
|
|
|
815 |
*/
|
|
|
816 |
if (in[0] == '.' && in[1] == '.' && in[2] == '/') {
|
|
|
817 |
/* remove "../" */
|
|
|
818 |
in += 3;
|
|
|
819 |
continue;
|
|
|
820 |
}
|
|
|
821 |
if (in[0] == '.' && in[1] == '/') {
|
|
|
822 |
/* remove "./" */
|
|
|
823 |
in += 2;
|
|
|
824 |
continue;
|
|
|
825 |
}
|
|
|
826 |
/*
|
|
|
827 |
B. if the input buffer begins with a prefix of "/./" or "/.",
|
|
|
828 |
where "." is a complete path segment, then replace that
|
|
|
829 |
prefix with "/" in the input buffer; otherwise,
|
|
|
830 |
*/
|
|
|
831 |
if (in[0] == '/' && in[1] == '.' && in[2] == '/') {
|
|
|
832 |
/* replace "/./" by "/" */
|
|
|
833 |
in += 2;
|
|
|
834 |
continue;
|
|
|
835 |
}
|
|
|
836 |
if (in[0] == '/' && in[1] == '.' && in[2] == '\0') {
|
|
|
837 |
/* replace trailing "/." by "/" */
|
|
|
838 |
in[1] = '\0';
|
|
|
839 |
continue;
|
|
|
840 |
}
|
|
|
841 |
/*
|
|
|
842 |
C. if the input buffer begins with a prefix of "/../" or "/..",
|
|
|
843 |
where ".." is a complete path segment, then replace that
|
|
|
844 |
prefix with "/" in the input buffer and remove the last
|
|
|
845 |
segment and its preceding "/" (if any) from the output
|
|
|
846 |
buffer; otherwise,
|
|
|
847 |
*/
|
|
|
848 |
if (in[0] == '/' && in[1] == '.' && in[2] == '.' && in[3] == '/') {
|
|
|
849 |
/* replace "/../" by "/" */
|
|
|
850 |
in += 3;
|
|
|
851 |
/* remove trailing "/segment" from output */
|
|
|
852 |
while(out > outbuf && *out != '/') out--;
|
|
|
853 |
*out = '\0';
|
|
|
854 |
continue;
|
|
|
855 |
}
|
|
|
856 |
if (in[0] == '/' && in[1] == '.' && in[2] == '.' && in[3] == '\0') {
|
|
|
857 |
/* replace trailing "/.." by "/" */
|
|
|
858 |
in[1] = '\0';
|
|
|
859 |
/* remove trailing "/segment" from output */
|
|
|
860 |
while(out > outbuf && *out != '/') out--;
|
|
|
861 |
*out = '\0';
|
|
|
862 |
continue;
|
|
|
863 |
}
|
|
|
864 |
/*
|
|
|
865 |
D. if the input buffer consists only of "." or "..", then remove
|
|
|
866 |
that from the input buffer; otherwise,
|
|
|
867 |
*/
|
|
|
868 |
if ( (in[0] == '.' && in[1] == '\0') ||
|
|
|
869 |
(in[0] == '.' && in[1] == '.' && in[2] == '\0') ) {
|
|
|
870 |
/* remove input */
|
|
|
871 |
in[0] = '\0';
|
|
|
872 |
continue;
|
|
|
873 |
}
|
|
|
874 |
/*
|
|
|
875 |
E. move the first path segment in the input buffer to the end of
|
|
|
876 |
the output buffer, including the initial "/" character (if
|
|
|
877 |
any) and any subsequent characters up to, but not including,
|
|
|
878 |
the next "/" character or the end of the input buffer.
|
|
|
879 |
*/
|
|
|
880 |
if (in[0] == '/') {
|
|
|
881 |
*out++ = '/';
|
|
|
882 |
in++;
|
|
|
883 |
}
|
|
|
884 |
for(; *in && *in != '/'; in++) *out++ = *in;
|
|
|
885 |
*out = '\0';
|
|
|
886 |
}
|
|
|
887 |
|
|
|
888 |
free(inbuf);
|
|
|
889 |
return outbuf;
|
|
|
890 |
}
|
|
|
891 |
|
| 49434 |
ripley |
892 |
/* this function is called to fetch new data from the client
|
|
|
893 |
* connection socket and process it */
|
| 48990 |
urbaneks |
894 |
static void worker_input_handler(void *data) {
|
|
|
895 |
httpd_conn_t *c = (httpd_conn_t*) data;
|
| 49434 |
ripley |
896 |
|
| 48990 |
urbaneks |
897 |
DBG(printf("worker_input_handler, data=%p\n", data));
|
|
|
898 |
if (!c) return;
|
| 49434 |
ripley |
899 |
|
| 78421 |
kalibera |
900 |
#ifndef _WIN32
|
| 59338 |
urbaneks |
901 |
if (in_process) return; /* we don't allow recursive entrance */
|
| 78421 |
kalibera |
902 |
#endif
|
| 59338 |
urbaneks |
903 |
|
| 48990 |
urbaneks |
904 |
DBG(printf("input handler for worker %p (sock=%d, part=%d, method=%d, line_pos=%d)\n", (void*) c, (int)c->sock, (int)c->part, (int)c->method, (int)c->line_pos));
|
| 49434 |
ripley |
905 |
|
|
|
906 |
/* FIXME: there is one edge case that is not caught on unix: if
|
|
|
907 |
* recv reads two or more full requests into the line buffer then
|
|
|
908 |
* this function exits after the first one, but input handlers may
|
|
|
909 |
* not trigger, because there may be no further data. It is not
|
|
|
910 |
* trivial to fix, because just checking for a full line at the
|
|
|
911 |
* beginning and not calling recv won't trigger a new input
|
|
|
912 |
* handler. However, under normal circumstance this should not
|
|
|
913 |
* happen, because clients should wait for the response and even
|
|
|
914 |
* if they don't it's unlikely that both requests get combined
|
|
|
915 |
* into one packet. */
|
| 48990 |
urbaneks |
916 |
if (c->part < PART_BODY) {
|
|
|
917 |
char *s = c->line_buf;
|
| 59184 |
ripley |
918 |
ssize_t n = recv(c->sock, c->line_buf + c->line_pos,
|
|
|
919 |
LINE_BUF_SIZE - c->line_pos - 1, 0);
|
| 86510 |
kalibera |
920 |
DBG(printf("[recv n=%lld, line_pos=%llu, part=%d]\n", (long long)n,
|
|
|
921 |
(unsigned long long)c->line_pos, (int)c->part));
|
| 48990 |
urbaneks |
922 |
if (n < 0) { /* error, scrape this worker */
|
|
|
923 |
remove_worker(c);
|
|
|
924 |
return;
|
|
|
925 |
}
|
|
|
926 |
if (n == 0) { /* connection closed -> try to process and then remove */
|
|
|
927 |
process_request(c);
|
|
|
928 |
remove_worker(c);
|
|
|
929 |
return;
|
|
|
930 |
}
|
|
|
931 |
c->line_pos += n;
|
|
|
932 |
c->line_buf[c->line_pos] = 0;
|
|
|
933 |
DBG(printf("in buffer: {%s}\n", c->line_buf));
|
|
|
934 |
while (*s) {
|
|
|
935 |
/* ok, we have genuine data in the line buffer */
|
|
|
936 |
if (s[0] == '\n' || (s[0] == '\r' && s[1] == '\n')) { /* single, empty line - end of headers */
|
|
|
937 |
/* --- check request validity --- */
|
|
|
938 |
DBG(printf(" end of request, moving to body\n"));
|
|
|
939 |
if (!(c->attr & HTTP_1_0) && !(c->attr & HOST_HEADER)) { /* HTTP/1.1 mandates Host: header */
|
| 49887 |
urbaneks |
940 |
send_http_response(c, " 400 Bad Request (Host: missing)\r\nConnection: close\r\n\r\n");
|
| 48990 |
urbaneks |
941 |
remove_worker(c);
|
|
|
942 |
return;
|
|
|
943 |
}
|
| 51562 |
urbaneks |
944 |
if (c->attr & CONTENT_LENGTH && c->content_length) {
|
| 52878 |
urbaneks |
945 |
if (c->content_length < 0 || /* we are parsing signed so negative numbers are bad */
|
|
|
946 |
c->content_length > 2147483640 || /* R will currently have issues with body around 2Gb or more, so better to not go there */
|
|
|
947 |
!(c->body = (char*) malloc(c->content_length + 1 /* allocate an extra termination byte */ ))) {
|
| 51562 |
urbaneks |
948 |
send_http_response(c, " 413 Request Entity Too Large (request body too big)\r\nConnection: close\r\n\r\n");
|
|
|
949 |
remove_worker(c);
|
|
|
950 |
return;
|
|
|
951 |
}
|
| 48990 |
urbaneks |
952 |
}
|
|
|
953 |
c->body_pos = 0;
|
|
|
954 |
c->part = PART_BODY;
|
|
|
955 |
if (s[0] == '\r') s++;
|
|
|
956 |
s++;
|
|
|
957 |
/* move the body part to the beginning of the buffer */
|
|
|
958 |
c->line_pos -= s - c->line_buf;
|
|
|
959 |
memmove(c->line_buf, s, c->line_pos);
|
| 62724 |
urbaneks |
960 |
/* GET/HEAD or no content length mean no body */
|
|
|
961 |
if (c->method == METHOD_GET || c->method == METHOD_HEAD ||
|
|
|
962 |
!(c->attr & CONTENT_LENGTH) || c->content_length == 0) {
|
| 62770 |
urbaneks |
963 |
if ((c->attr & CONTENT_LENGTH) && c->content_length > 0) {
|
| 51805 |
urbaneks |
964 |
send_http_response(c, " 400 Bad Request (GET/HEAD with body)\r\n\r\n");
|
| 48990 |
urbaneks |
965 |
remove_worker(c);
|
|
|
966 |
return;
|
|
|
967 |
}
|
|
|
968 |
process_request(c);
|
|
|
969 |
if (c->attr & CONNECTION_CLOSE) {
|
|
|
970 |
remove_worker(c);
|
|
|
971 |
return;
|
|
|
972 |
}
|
|
|
973 |
/* keep-alive - reset the worker so it can process a new request */
|
|
|
974 |
if (c->url) { free(c->url); c->url = NULL; }
|
|
|
975 |
if (c->body) { free(c->body); c->body = NULL; }
|
| 51562 |
urbaneks |
976 |
if (c->content_type) { free(c->content_type); c->content_type = NULL; }
|
| 54055 |
urbaneks |
977 |
if (c->headers) { free_buffer(c->headers); c->headers = NULL; }
|
| 48990 |
urbaneks |
978 |
c->body_pos = 0;
|
|
|
979 |
c->method = 0;
|
|
|
980 |
c->part = PART_REQUEST;
|
|
|
981 |
c->attr = 0;
|
|
|
982 |
c->content_length = 0;
|
|
|
983 |
return;
|
|
|
984 |
}
|
| 51653 |
urbaneks |
985 |
/* copy body content (as far as available) */
|
|
|
986 |
c->body_pos = (c->content_length < c->line_pos) ? c->content_length : c->line_pos;
|
|
|
987 |
if (c->body_pos) {
|
|
|
988 |
memcpy(c->body, c->line_buf, c->body_pos);
|
|
|
989 |
c->line_pos -= c->body_pos; /* NOTE: we are NOT moving the buffer since non-zero left-over causes connection close */
|
|
|
990 |
}
|
| 48990 |
urbaneks |
991 |
/* POST will continue into the BODY part */
|
|
|
992 |
break;
|
|
|
993 |
}
|
|
|
994 |
{
|
|
|
995 |
char *bol = s;
|
|
|
996 |
while (*s && *s != '\r' && *s != '\n') s++;
|
|
|
997 |
if (!*s) { /* incomplete line */
|
|
|
998 |
if (bol == c->line_buf) {
|
|
|
999 |
if (c->line_pos < LINE_BUF_SIZE) /* one, incomplete line, but the buffer is not full yet, just return */
|
|
|
1000 |
return;
|
|
|
1001 |
/* the buffer is full yet the line is incomplete - we're in trouble */
|
| 51805 |
urbaneks |
1002 |
send_http_response(c, " 413 Request entity too large\r\nConnection: close\r\n\r\n");
|
| 48990 |
urbaneks |
1003 |
remove_worker(c);
|
|
|
1004 |
return;
|
|
|
1005 |
}
|
| 84215 |
maechler |
1006 |
/* move the line to the beginning of the buffer for later requests */
|
| 48990 |
urbaneks |
1007 |
c->line_pos -= bol - c->line_buf;
|
|
|
1008 |
memmove(c->line_buf, bol, c->line_pos);
|
|
|
1009 |
return;
|
|
|
1010 |
} else { /* complete line, great! */
|
|
|
1011 |
if (*s == '\r') *(s++) = 0;
|
|
|
1012 |
if (*s == '\n') *(s++) = 0;
|
|
|
1013 |
DBG(printf("complete line: {%s}\n", bol));
|
|
|
1014 |
if (c->part == PART_REQUEST) {
|
|
|
1015 |
/* --- process request line --- */
|
| 59184 |
ripley |
1016 |
size_t rll = strlen(bol); /* request line length */
|
| 62724 |
urbaneks |
1017 |
char *url = strchr(bol, ' ');
|
|
|
1018 |
if (!url || rll < 14 || strncmp(bol + rll - 9, " HTTP/1.", 8)) { /* each request must have at least 14 characters [GET / HTTP/1.0] and have HTTP/1.x */
|
| 51805 |
urbaneks |
1019 |
send_response(c->sock, "HTTP/1.0 400 Bad Request\r\n\r\n", 28);
|
| 48990 |
urbaneks |
1020 |
remove_worker(c);
|
|
|
1021 |
return;
|
|
|
1022 |
}
|
| 62724 |
urbaneks |
1023 |
url++;
|
| 79433 |
kalibera |
1024 |
bol[strlen(bol) - 9] = 0; /* cut off " HTTP/1.x" */
|
|
|
1025 |
c->url = remove_dot_segments(url);
|
| 48990 |
urbaneks |
1026 |
if (!strncmp(bol + rll - 3, "1.0", 3)) c->attr |= HTTP_1_0;
|
| 62724 |
urbaneks |
1027 |
if (!strncmp(bol, "GET ", 4)) c->method = METHOD_GET;
|
| 48990 |
urbaneks |
1028 |
if (!strncmp(bol, "POST ", 5)) c->method = METHOD_POST;
|
|
|
1029 |
if (!strncmp(bol, "HEAD ", 5)) c->method = METHOD_HEAD;
|
| 62724 |
urbaneks |
1030 |
/* only custom handlers can use other methods */
|
| 79433 |
kalibera |
1031 |
if (!strncmp(c->url, "/custom/", 8)) {
|
| 62724 |
urbaneks |
1032 |
char *mend = url - 1;
|
|
|
1033 |
/* we generate a header with the method so it can be passed to the handler */
|
|
|
1034 |
if (!c->headers)
|
|
|
1035 |
c->headers = alloc_buffer(1024, NULL);
|
|
|
1036 |
/* make sure it fits */
|
|
|
1037 |
if (c->headers->size - c->headers->length >= 18 + (mend - bol)) {
|
| 62726 |
urbaneks |
1038 |
if (!c->method) c->method = METHOD_OTHER;
|
| 62724 |
urbaneks |
1039 |
/* add "Request-Method: xxx" */
|
|
|
1040 |
memcpy(c->headers->data + c->headers->length, "Request-Method: ", 16);
|
|
|
1041 |
c->headers->length += 16;
|
| 87180 |
kalibera |
1042 |
if (mend - bol)
|
|
|
1043 |
memcpy(c->headers->data + c->headers->length, bol, mend - bol);
|
| 62724 |
urbaneks |
1044 |
c->headers->length += mend - bol;
|
|
|
1045 |
c->headers->data[c->headers->length++] = '\n';
|
|
|
1046 |
}
|
|
|
1047 |
}
|
| 48990 |
urbaneks |
1048 |
if (!c->method) {
|
| 51805 |
urbaneks |
1049 |
send_http_response(c, " 501 Invalid or unimplemented method\r\n\r\n");
|
| 48990 |
urbaneks |
1050 |
remove_worker(c);
|
|
|
1051 |
return;
|
|
|
1052 |
}
|
|
|
1053 |
c->part = PART_HEADER;
|
|
|
1054 |
DBG(printf("parsed request, method=%d, URL='%s'\n", (int)c->method, c->url));
|
|
|
1055 |
} else if (c->part == PART_HEADER) {
|
|
|
1056 |
/* --- process headers --- */
|
|
|
1057 |
char *k = bol;
|
| 54055 |
urbaneks |
1058 |
if (!c->headers)
|
|
|
1059 |
c->headers = alloc_buffer(1024, NULL);
|
|
|
1060 |
if (c->headers) { /* record the header line in the buffer */
|
| 59184 |
ripley |
1061 |
size_t l = strlen(bol);
|
| 54055 |
urbaneks |
1062 |
if (l) { /* this should be really always true */
|
|
|
1063 |
if (c->headers->length + l + 1 > c->headers->size) { /* not enough space? */
|
| 59184 |
ripley |
1064 |
size_t fits = c->headers->size - c->headers->length;
|
| 54055 |
urbaneks |
1065 |
if (fits) memcpy(c->headers->data + c->headers->length, bol, fits);
|
|
|
1066 |
if (alloc_buffer(2048, c->headers)) {
|
|
|
1067 |
c->headers = c->headers->next;
|
| 87180 |
kalibera |
1068 |
if (l - fits) memcpy(c->headers->data, bol + fits, l - fits);
|
| 54055 |
urbaneks |
1069 |
c->headers->length = l - fits;
|
|
|
1070 |
c->headers->data[c->headers->length++] = '\n';
|
|
|
1071 |
}
|
|
|
1072 |
} else {
|
|
|
1073 |
memcpy(c->headers->data + c->headers->length, bol, l);
|
|
|
1074 |
c->headers->length += l;
|
|
|
1075 |
c->headers->data[c->headers->length++] = '\n';
|
|
|
1076 |
}
|
|
|
1077 |
}
|
|
|
1078 |
}
|
| 48990 |
urbaneks |
1079 |
while (*k && *k != ':') {
|
|
|
1080 |
if (*k >= 'A' && *k <= 'Z')
|
|
|
1081 |
*k |= 0x20;
|
|
|
1082 |
k++;
|
|
|
1083 |
}
|
|
|
1084 |
if (*k == ':') {
|
|
|
1085 |
*(k++) = 0;
|
|
|
1086 |
while (*k == ' ' || *k == '\t') k++;
|
|
|
1087 |
DBG(printf("header '%s' => '%s'\n", bol, k));
|
|
|
1088 |
if (!strcmp(bol, "content-length")) {
|
|
|
1089 |
c->attr |= CONTENT_LENGTH;
|
| 52878 |
urbaneks |
1090 |
c->content_length = atol(k);
|
| 48990 |
urbaneks |
1091 |
}
|
| 51562 |
urbaneks |
1092 |
if (!strcmp(bol, "content-type")) {
|
|
|
1093 |
char *l = k;
|
| 69420 |
urbaneks |
1094 |
/* convert content-type to lowercase to facilitate comparison
|
|
|
1095 |
since MIME types are case-insensitive.
|
|
|
1096 |
However, we have to stop at ; since parameters
|
|
|
1097 |
may be case-sensitive (see PR 16541) */
|
|
|
1098 |
while (*l && *l != ';') { if (*l >= 'A' && *l <= 'Z') *l |= 0x20; l++; }
|
| 51562 |
urbaneks |
1099 |
c->attr |= CONTENT_TYPE;
|
|
|
1100 |
if (c->content_type) free(c->content_type);
|
| 78536 |
ripley |
1101 |
c->content_type = Rstrdup(k);
|
| 51562 |
urbaneks |
1102 |
if (!strncmp(k, "application/x-www-form-urlencoded", 33))
|
|
|
1103 |
c->attr |= CONTENT_FORM_UENC;
|
|
|
1104 |
}
|
| 48990 |
urbaneks |
1105 |
if (!strcmp(bol, "host"))
|
|
|
1106 |
c->attr |= HOST_HEADER;
|
|
|
1107 |
if (!strcmp(bol, "connection")) {
|
|
|
1108 |
char *l = k;
|
|
|
1109 |
while (*l) { if (*l >= 'A' && *l <= 'Z') *l |= 0x20; l++; }
|
|
|
1110 |
if (!strncmp(k, "close", 5))
|
| 54979 |
urbaneks |
1111 |
c->attr |= CONNECTION_CLOSE;
|
| 48990 |
urbaneks |
1112 |
}
|
|
|
1113 |
}
|
|
|
1114 |
}
|
|
|
1115 |
}
|
|
|
1116 |
}
|
|
|
1117 |
}
|
| 51653 |
urbaneks |
1118 |
if (c->part < PART_BODY) {
|
|
|
1119 |
/* we end here if we processed a buffer of exactly one line */
|
|
|
1120 |
c->line_pos = 0;
|
| 48990 |
urbaneks |
1121 |
return;
|
|
|
1122 |
}
|
| 51653 |
urbaneks |
1123 |
}
|
|
|
1124 |
if (c->part == PART_BODY && c->body) { /* BODY - this branch always returns */
|
|
|
1125 |
if (c->body_pos < c->content_length) { /* need to receive more ? */
|
| 86510 |
kalibera |
1126 |
DBG(printf("BODY: body_pos=%llu, content_length=%ld\n",
|
|
|
1127 |
(unsigned long long)c->body_pos, c->content_length));
|
| 59184 |
ripley |
1128 |
ssize_t n = recv(c->sock, c->body + c->body_pos,
|
|
|
1129 |
c->content_length - c->body_pos, 0);
|
| 86510 |
kalibera |
1130 |
DBG(printf(" [recv n=%lld - had %llu of %lu]\n", (long long)n,
|
|
|
1131 |
(unsigned long long)c->body_pos, c->content_length));
|
| 51653 |
urbaneks |
1132 |
c->line_pos = 0;
|
|
|
1133 |
if (n < 0) { /* error, scrap this worker */
|
|
|
1134 |
remove_worker(c);
|
|
|
1135 |
return;
|
|
|
1136 |
}
|
|
|
1137 |
if (n == 0) { /* connection closed -> try to process and then remove */
|
|
|
1138 |
process_request(c);
|
| 48990 |
urbaneks |
1139 |
remove_worker(c);
|
| 51653 |
urbaneks |
1140 |
return;
|
|
|
1141 |
}
|
|
|
1142 |
c->body_pos += n;
|
| 48990 |
urbaneks |
1143 |
}
|
|
|
1144 |
if (c->body_pos == c->content_length) { /* yay! we got the whole body */
|
|
|
1145 |
process_request(c);
|
| 51653 |
urbaneks |
1146 |
if (c->attr & CONNECTION_CLOSE || c->line_pos) { /* we have to close the connection if there was a double-hit */
|
| 48990 |
urbaneks |
1147 |
remove_worker(c);
|
|
|
1148 |
return;
|
|
|
1149 |
}
|
|
|
1150 |
/* keep-alive - reset the worker so it can process a new request */
|
|
|
1151 |
if (c->url) { free(c->url); c->url = NULL; }
|
|
|
1152 |
if (c->body) { free(c->body); c->body = NULL; }
|
| 51562 |
urbaneks |
1153 |
if (c->content_type) { free(c->content_type); c->content_type = NULL; }
|
| 54327 |
urbaneks |
1154 |
if (c->headers) { free_buffer(c->headers); c->headers = NULL; }
|
| 48990 |
urbaneks |
1155 |
c->line_pos = 0; c->body_pos = 0;
|
|
|
1156 |
c->method = 0;
|
|
|
1157 |
c->part = PART_REQUEST;
|
|
|
1158 |
c->attr = 0;
|
|
|
1159 |
c->content_length = 0;
|
|
|
1160 |
return;
|
|
|
1161 |
}
|
|
|
1162 |
}
|
| 49434 |
ripley |
1163 |
|
| 48990 |
urbaneks |
1164 |
/* we enter here only if recv was used to leave the headers with no body */
|
|
|
1165 |
if (c->part == PART_BODY && !c->body) {
|
|
|
1166 |
char *s = c->line_buf;
|
|
|
1167 |
if (c->line_pos > 0) {
|
|
|
1168 |
if ((s[0] != '\r' || s[1] != '\n') && (s[0] != '\n')) {
|
| 49887 |
urbaneks |
1169 |
send_http_response(c, " 411 length is required for non-empty body\r\nConnection: close\r\n\r\n");
|
| 48990 |
urbaneks |
1170 |
remove_worker(c);
|
|
|
1171 |
return;
|
|
|
1172 |
}
|
|
|
1173 |
/* empty body, good */
|
|
|
1174 |
process_request(c);
|
|
|
1175 |
if (c->attr & CONNECTION_CLOSE) {
|
|
|
1176 |
remove_worker(c);
|
|
|
1177 |
return;
|
|
|
1178 |
} else { /* keep-alive */
|
|
|
1179 |
int sh = 1;
|
|
|
1180 |
if (s[0] == '\r') sh++;
|
|
|
1181 |
if (c->line_pos <= sh)
|
|
|
1182 |
c->line_pos = 0;
|
|
|
1183 |
else { /* shift the remaining buffer */
|
|
|
1184 |
memmove(c->line_buf, c->line_buf + sh, c->line_pos - sh);
|
|
|
1185 |
c->line_pos -= sh;
|
|
|
1186 |
}
|
|
|
1187 |
/* keep-alive - reset the worker so it can process a new request */
|
|
|
1188 |
if (c->url) { free(c->url); c->url = NULL; }
|
|
|
1189 |
if (c->body) { free(c->body); c->body = NULL; }
|
| 51562 |
urbaneks |
1190 |
if (c->content_type) { free(c->content_type); c->content_type = NULL; }
|
| 54327 |
urbaneks |
1191 |
if (c->headers) { free_buffer(c->headers); c->headers = NULL; }
|
| 48990 |
urbaneks |
1192 |
c->body_pos = 0;
|
|
|
1193 |
c->method = 0;
|
|
|
1194 |
c->part = PART_REQUEST;
|
|
|
1195 |
c->attr = 0;
|
|
|
1196 |
c->content_length = 0;
|
|
|
1197 |
return;
|
|
|
1198 |
}
|
|
|
1199 |
}
|
| 59184 |
ripley |
1200 |
ssize_t n = recv(c->sock, c->line_buf + c->line_pos,
|
|
|
1201 |
LINE_BUF_SIZE - c->line_pos - 1, 0);
|
| 51562 |
urbaneks |
1202 |
if (n < 0) { /* error, scrap this worker */
|
| 48990 |
urbaneks |
1203 |
remove_worker(c);
|
|
|
1204 |
return;
|
|
|
1205 |
}
|
|
|
1206 |
if (n == 0) { /* connection closed -> try to process and then remove */
|
|
|
1207 |
process_request(c);
|
|
|
1208 |
remove_worker(c);
|
|
|
1209 |
return;
|
|
|
1210 |
}
|
|
|
1211 |
if ((s[0] != '\r' || s[1] != '\n') && (s[0] != '\n')) {
|
| 49887 |
urbaneks |
1212 |
send_http_response(c, " 411 length is required for non-empty body\r\nConnection: close\r\n\r\n");
|
| 48990 |
urbaneks |
1213 |
remove_worker(c);
|
|
|
1214 |
return;
|
| 49434 |
ripley |
1215 |
}
|
| 48990 |
urbaneks |
1216 |
}
|
|
|
1217 |
}
|
|
|
1218 |
|
| 85800 |
ripley |
1219 |
static SOCKET srv_sock = INVALID_SOCKET;
|
|
|
1220 |
|
|
|
1221 |
#ifdef _WIN32
|
| 48990 |
urbaneks |
1222 |
static void srv_input_handler(void *data);
|
| 85799 |
kalibera |
1223 |
static WSAEVENT server_thread_should_stop = NULL;
|
| 48990 |
urbaneks |
1224 |
|
| 49434 |
ripley |
1225 |
/* Windows implementation uses threads to accept and serve
|
|
|
1226 |
connections, using the main event loop to synchronize with R
|
|
|
1227 |
through a message-only window which is created on the R thread
|
|
|
1228 |
*/
|
| 48990 |
urbaneks |
1229 |
static LRESULT CALLBACK RhttpdWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
|
1230 |
{
|
| 86511 |
kalibera |
1231 |
DBG(printf("RhttpdWindowProc(%p, %x, %x, %x)\n", (void *) hwnd, (int) uMsg, (int) wParam, (int) lParam));
|
| 48990 |
urbaneks |
1232 |
if (hwnd == message_window && uMsg == WM_RHTTP_CALLBACK) {
|
|
|
1233 |
httpd_conn_t *c = (httpd_conn_t*) lParam;
|
|
|
1234 |
process_request_main_thread(c);
|
|
|
1235 |
return 0;
|
|
|
1236 |
}
|
|
|
1237 |
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
|
1238 |
}
|
|
|
1239 |
|
| 49434 |
ripley |
1240 |
/* server thread - accepts connections on the server socket and
|
|
|
1241 |
creates worker threads
|
|
|
1242 |
*/
|
| 48990 |
urbaneks |
1243 |
static DWORD WINAPI ServerThreadProc(LPVOID lpParameter) {
|
| 85799 |
kalibera |
1244 |
|
|
|
1245 |
WSAEVENT srv_sock_ready = WSACreateEvent();
|
|
|
1246 |
if (!srv_sock_ready ||
|
|
|
1247 |
WSAEventSelect(srv_sock, srv_sock_ready, FD_ACCEPT)) {
|
|
|
1248 |
|
|
|
1249 |
return 1;
|
| 48990 |
urbaneks |
1250 |
}
|
| 85799 |
kalibera |
1251 |
|
|
|
1252 |
WSAEVENT events[] = { srv_sock_ready, server_thread_should_stop };
|
|
|
1253 |
for(;;) {
|
|
|
1254 |
DWORD ret = WSAWaitForMultipleEvents(2, events, FALSE, WSA_INFINITE,
|
|
|
1255 |
FALSE);
|
|
|
1256 |
if (ret == WSA_WAIT_EVENT_0) {
|
|
|
1257 |
WSANETWORKEVENTS events;
|
|
|
1258 |
/* only re-set, we know the event is FD_ACCEPT */
|
|
|
1259 |
WSAEnumNetworkEvents(srv_sock, srv_sock_ready, &events);
|
|
|
1260 |
srv_input_handler(lpParameter);
|
|
|
1261 |
continue;
|
|
|
1262 |
}
|
|
|
1263 |
/* exit the thread when signalled to do so or on error */
|
|
|
1264 |
break;
|
|
|
1265 |
}
|
|
|
1266 |
|
| 48990 |
urbaneks |
1267 |
return 0;
|
|
|
1268 |
}
|
|
|
1269 |
|
|
|
1270 |
/* worker thread - processes one client connection socket */
|
|
|
1271 |
static DWORD WINAPI WorkerThreadProc(LPVOID lpParameter) {
|
|
|
1272 |
httpd_conn_t *c = (httpd_conn_t*) lpParameter;
|
|
|
1273 |
if (!c) return 0;
|
|
|
1274 |
while ((c->attr & THREAD_DISPOSE) == 0) {
|
|
|
1275 |
c->attr |= THREAD_OWNED; /* make sure the worker is not removed by the handler since we need it */
|
|
|
1276 |
worker_input_handler(c);
|
|
|
1277 |
}
|
|
|
1278 |
/* the handler signalled a desire to remove the worker, do it */
|
|
|
1279 |
c->attr = 0; /* reset the flags */
|
|
|
1280 |
remove_worker(c); /* free the worker */
|
|
|
1281 |
return 0;
|
|
|
1282 |
}
|
|
|
1283 |
|
|
|
1284 |
/* global server thread - currently we support only one server at a time */
|
| 85799 |
kalibera |
1285 |
static HANDLE server_thread = NULL;
|
| 48990 |
urbaneks |
1286 |
#else
|
| 49434 |
ripley |
1287 |
/* on unix we register all used sockets (server and workers) as input
|
|
|
1288 |
* handlers such that we can avoid polling */
|
| 48990 |
urbaneks |
1289 |
|
|
|
1290 |
/* global input handler for the server socket */
|
|
|
1291 |
static InputHandler *srv_handler;
|
|
|
1292 |
#endif
|
|
|
1293 |
|
| 49434 |
ripley |
1294 |
static void srv_input_handler(void *data)
|
|
|
1295 |
{
|
| 48990 |
urbaneks |
1296 |
httpd_conn_t *c;
|
|
|
1297 |
SAIN peer_sa;
|
|
|
1298 |
socklen_t peer_sal = sizeof(peer_sa);
|
|
|
1299 |
SOCKET cl_sock = accept(srv_sock, (SA*) &peer_sa, &peer_sal);
|
|
|
1300 |
if (cl_sock == INVALID_SOCKET) /* accept failed, don't bother */
|
|
|
1301 |
return;
|
|
|
1302 |
c = (httpd_conn_t*) calloc(1, sizeof(httpd_conn_t));
|
| 78455 |
ripley |
1303 |
if (c == NULL) error("allocation error in srv_input_handler");
|
| 48990 |
urbaneks |
1304 |
c->sock = cl_sock;
|
|
|
1305 |
c->peer = peer_sa.sin_addr;
|
| 65805 |
ripley |
1306 |
#ifndef _WIN32
|
| 49434 |
ripley |
1307 |
c->ih = addInputHandler(R_InputHandlers, cl_sock, &worker_input_handler,
|
|
|
1308 |
HttpdWorkerActivity);
|
| 48990 |
urbaneks |
1309 |
if (c->ih) c->ih->userData = c;
|
|
|
1310 |
add_worker(c);
|
|
|
1311 |
#else
|
| 86509 |
kalibera |
1312 |
/* The accepted socket inherits properties of the socket listened to.
|
|
|
1313 |
The server socket is non-blocking, because WSAEventSelect has been used on it.
|
|
|
1314 |
Make sure the accepted socket is blocking. */
|
|
|
1315 |
WSAEventSelect(c->sock, NULL, 0);
|
|
|
1316 |
unsigned long mode = 0;
|
|
|
1317 |
ioctlsocket(c->sock, FIONBIO, &mode);
|
|
|
1318 |
|
| 49434 |
ripley |
1319 |
if (!add_worker(c)) { /* create worker thread only if the worker
|
|
|
1320 |
* was accepted */
|
|
|
1321 |
if (!(c->thread = CreateThread(NULL, 0, WorkerThreadProc,
|
|
|
1322 |
(LPVOID) c, 0, 0)))
|
| 48990 |
urbaneks |
1323 |
remove_worker(c);
|
|
|
1324 |
}
|
|
|
1325 |
#endif
|
|
|
1326 |
}
|
|
|
1327 |
|
| 85799 |
kalibera |
1328 |
#ifdef _WIN32
|
|
|
1329 |
void stop_server_thread(void)
|
|
|
1330 |
{
|
|
|
1331 |
if (!server_thread || !server_thread_should_stop)
|
|
|
1332 |
return;
|
|
|
1333 |
WSASetEvent(server_thread_should_stop);
|
|
|
1334 |
WaitForSingleObject(server_thread, INFINITE);
|
|
|
1335 |
WSACloseEvent(server_thread_should_stop);
|
|
|
1336 |
CloseHandle(server_thread);
|
|
|
1337 |
server_thread = NULL;
|
|
|
1338 |
server_thread_should_stop = NULL;
|
|
|
1339 |
}
|
|
|
1340 |
#endif
|
|
|
1341 |
|
| 49434 |
ripley |
1342 |
int in_R_HTTPDCreate(const char *ip, int port)
|
|
|
1343 |
{
|
| 65805 |
ripley |
1344 |
#ifndef _WIN32
|
| 48990 |
urbaneks |
1345 |
int reuse = 1;
|
| 49788 |
ripley |
1346 |
#endif
|
| 48990 |
urbaneks |
1347 |
SAIN srv_sa;
|
| 49434 |
ripley |
1348 |
|
| 48990 |
urbaneks |
1349 |
if (needs_init) /* initialization may need to be performed on first use */
|
|
|
1350 |
first_init();
|
| 49434 |
ripley |
1351 |
|
| 85799 |
kalibera |
1352 |
#ifdef _WIN32
|
|
|
1353 |
/* on Windows stop the server thread if it exists */
|
|
|
1354 |
stop_server_thread();
|
|
|
1355 |
#endif
|
|
|
1356 |
|
| 48990 |
urbaneks |
1357 |
/* is already in use, close the current socket */
|
|
|
1358 |
if (srv_sock != INVALID_SOCKET)
|
|
|
1359 |
closesocket(srv_sock);
|
| 49434 |
ripley |
1360 |
|
| 48990 |
urbaneks |
1361 |
/* create a new socket */
|
|
|
1362 |
srv_sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
1363 |
if (srv_sock == INVALID_SOCKET)
|
|
|
1364 |
Rf_error("unable to create socket");
|
| 49434 |
ripley |
1365 |
|
| 65805 |
ripley |
1366 |
#ifndef _WIN32
|
| 48990 |
urbaneks |
1367 |
/* set socket for reuse so we can re-init if we die */
|
| 49754 |
murdoch |
1368 |
/* But on Windows, this lets us stomp on any port already in use, so don't do it. */
|
| 49434 |
ripley |
1369 |
setsockopt(srv_sock, SOL_SOCKET, SO_REUSEADDR,
|
|
|
1370 |
(const char*)&reuse, sizeof(reuse));
|
| 49754 |
murdoch |
1371 |
#endif
|
| 49434 |
ripley |
1372 |
|
| 48990 |
urbaneks |
1373 |
/* bind to the desired port */
|
|
|
1374 |
if (bind(srv_sock, build_sin(&srv_sa, ip, port), sizeof(srv_sa))) {
|
| 80477 |
kalibera |
1375 |
#ifndef _WIN32
|
| 48990 |
urbaneks |
1376 |
if (sockerrno == EADDRINUSE) {
|
| 80477 |
kalibera |
1377 |
#else
|
|
|
1378 |
if (sockerrno == WSAEADDRINUSE) {
|
|
|
1379 |
#endif
|
| 48990 |
urbaneks |
1380 |
closesocket(srv_sock);
|
|
|
1381 |
srv_sock = INVALID_SOCKET;
|
|
|
1382 |
return -2;
|
|
|
1383 |
} else {
|
|
|
1384 |
closesocket(srv_sock);
|
|
|
1385 |
srv_sock = INVALID_SOCKET;
|
|
|
1386 |
Rf_error("unable to bind socket to TCP port %d", port);
|
|
|
1387 |
}
|
|
|
1388 |
}
|
| 49434 |
ripley |
1389 |
|
| 48990 |
urbaneks |
1390 |
/* setup listen */
|
| 85799 |
kalibera |
1391 |
if (listen(srv_sock, 8)) {
|
|
|
1392 |
closesocket(srv_sock);
|
|
|
1393 |
srv_sock = INVALID_SOCKET;
|
| 48990 |
urbaneks |
1394 |
Rf_error("cannot listen to TCP port %d", port);
|
| 85799 |
kalibera |
1395 |
}
|
| 49434 |
ripley |
1396 |
|
| 65805 |
ripley |
1397 |
#ifndef _WIN32
|
| 48990 |
urbaneks |
1398 |
/* all went well, register the socket as a handler */
|
|
|
1399 |
if (srv_handler) removeInputHandler(&R_InputHandlers, srv_handler);
|
| 49434 |
ripley |
1400 |
srv_handler = addInputHandler(R_InputHandlers, srv_sock,
|
|
|
1401 |
&srv_input_handler, HttpdServerActivity);
|
| 48990 |
urbaneks |
1402 |
#else
|
|
|
1403 |
/* do the desired Windows synchronization */
|
| 85799 |
kalibera |
1404 |
server_thread_should_stop = WSACreateEvent();
|
|
|
1405 |
if (!server_thread_should_stop) {
|
|
|
1406 |
closesocket(srv_sock);
|
|
|
1407 |
srv_sock = INVALID_SOCKET;
|
|
|
1408 |
Rf_error("cannot create synchronization event");
|
|
|
1409 |
}
|
|
|
1410 |
|
| 48990 |
urbaneks |
1411 |
server_thread = CreateThread(NULL, 0, ServerThreadProc, 0, 0, 0);
|
| 85799 |
kalibera |
1412 |
if (!server_thread) {
|
|
|
1413 |
closesocket(srv_sock);
|
|
|
1414 |
srv_sock = INVALID_SOCKET;
|
|
|
1415 |
WSACloseEvent(server_thread_should_stop);
|
|
|
1416 |
server_thread_should_stop = NULL;
|
|
|
1417 |
Rf_error("cannot create server thread");
|
|
|
1418 |
}
|
| 48990 |
urbaneks |
1419 |
#endif
|
|
|
1420 |
return 0;
|
|
|
1421 |
}
|
|
|
1422 |
|
| 49434 |
ripley |
1423 |
void in_R_HTTPDStop(void)
|
|
|
1424 |
{
|
| 65805 |
ripley |
1425 |
#ifdef _WIN32
|
| 49434 |
ripley |
1426 |
/* on Windows stop the server thread if it exists */
|
| 85799 |
kalibera |
1427 |
stop_server_thread();
|
|
|
1428 |
#endif
|
|
|
1429 |
|
|
|
1430 |
if (srv_sock != INVALID_SOCKET) {
|
|
|
1431 |
closesocket(srv_sock);
|
|
|
1432 |
srv_sock = INVALID_SOCKET;
|
| 49434 |
ripley |
1433 |
}
|
| 85799 |
kalibera |
1434 |
|
|
|
1435 |
#ifndef _WIN32
|
| 49434 |
ripley |
1436 |
if (srv_handler) removeInputHandler(&R_InputHandlers, srv_handler);
|
|
|
1437 |
#endif
|
|
|
1438 |
}
|
|
|
1439 |
|
|
|
1440 |
/* Create an internal http server in R. Note that currently there can
|
|
|
1441 |
only be at most one http server running at any given time so the
|
|
|
1442 |
behavior is undefined if a server already exists (currently any
|
|
|
1443 |
previous servers will be shut down by this call but the shutdown
|
|
|
1444 |
may not be clean).
|
|
|
1445 |
|
|
|
1446 |
@param sIP is the IP to bind to (or NULL for any)
|
|
|
1447 |
@param sPort is the TCP port number to bin to
|
|
|
1448 |
@return returns an integer value -- 0L on success, other values
|
|
|
1449 |
denote failures: -2L means that the address/port combination is
|
|
|
1450 |
already in use
|
|
|
1451 |
*/
|
|
|
1452 |
SEXP R_init_httpd(SEXP sIP, SEXP sPort)
|
|
|
1453 |
{
|
| 48990 |
urbaneks |
1454 |
const char *ip = 0;
|
| 82334 |
kalibera |
1455 |
const void *vmax = NULL;
|
|
|
1456 |
|
| 48990 |
urbaneks |
1457 |
if (sIP != R_NilValue && (TYPEOF(sIP) != STRSXP || LENGTH(sIP) != 1))
|
|
|
1458 |
Rf_error("invalid bind address specification");
|
| 82334 |
kalibera |
1459 |
vmax = vmaxget();
|
| 48990 |
urbaneks |
1460 |
if (sIP != R_NilValue)
|
| 82334 |
kalibera |
1461 |
ip = translateChar(STRING_ELT(sIP, 0));
|
|
|
1462 |
SEXP ans = ScalarInteger(in_R_HTTPDCreate(ip, asInteger(sPort)));
|
|
|
1463 |
vmaxset(vmax);
|
|
|
1464 |
return ans;
|
| 48990 |
urbaneks |
1465 |
}
|