comparison packages/net/athttpd/current/src/socket.c @ 2250:0830ae6acddc

Add ATHTTPD server from Anthony Tonizzo.
author jlarmour
date Tue, 18 Jul 2006 16:37:23 +0000
parents
children 3327c37c28d1
comparison
equal deleted inserted replaced
2249:6778c16dcfcd 2250:0830ae6acddc
1 /* =================================================================
2 *
3 * socket.c
4 *
5 * Opens socket and starts the daemon.
6 *
7 * =================================================================
8 * ####ECOSGPLCOPYRIGHTBEGIN####
9 * -------------------------------------------
10 * This file is part of eCos, the Embedded Configurable Operating
11 * System.
12 * Copyright (C) 2005 eCosCentric Ltd.
13 *
14 * eCos is free software; you can redistribute it and/or modify it
15 * under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 or (at your option)
17 * any later version.
18 *
19 * eCos is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with eCos; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
27 *
28 * As a special exception, if other files instantiate templates or
29 * use macros or inline functions from this file, or you compile this
30 * file and link it with other works to produce a work based on this
31 * file, this file does not by itself cause the resulting work to be
32 * covered by the GNU General Public License. However the source code
33 * for this file must still be made available in accordance with
34 * section (3) of the GNU General Public License.
35 *
36 * This exception does not invalidate any other reasons why a work
37 * based on this file might be covered by the GNU General Public
38 * License.
39 *
40 * -------------------------------------------
41 * ####ECOSGPLCOPYRIGHTEND####
42 * =================================================================
43 * #####DESCRIPTIONBEGIN####
44 *
45 * Author(s): Anthony Tonizzo (atonizzo@gmail.com)
46 * Contributors:
47 * Date: 2006-06-12
48 * Purpose:
49 * Description:
50 *
51 * ####DESCRIPTIONEND####
52 *
53 * =================================================================
54 */
55 #include <pkgconf/hal.h>
56 #include <pkgconf/kernel.h>
57 #include <cyg/kernel/kapi.h> // Kernel API.
58 #include <cyg/kernel/ktypes.h> // base kernel types.
59 #include <cyg/infra/diag.h> // For diagnostic printing.
60 #include <network.h>
61 #include <sys/uio.h>
62 #include <fcntl.h>
63 #include <stdio.h> // sprintf().
64 #include <time.h> // sprintf().
65
66 #include <cyg/athttpd/http.h>
67 #include <cyg/athttpd/socket.h>
68 #include <cyg/athttpd/cgi.h>
69
70 #define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
71 #define CYG_HTTPD_DAEMON_STACK_SIZE (CYGNUM_HAL_STACK_SIZE_MINIMUM + \
72 CYGNUM_NET_ATHTTPD_THREADOPT_STACKSIZE)
73 static cyg_int32 cyg_httpd_initialized = 0;
74 cyg_thread cyg_httpd_thread_object;
75 cyg_handle_t cyg_httpd_thread_handle;
76 cyg_uint8 cyg_httpd_thread_stack[CYG_HTTPD_DAEMON_STACK_SIZE]
77 __attribute__((__aligned__ (16)));
78 CYG_HTTPD_STATE httpstate;
79
80 __inline__ ssize_t
81 cyg_httpd_write(char* buf, int len)
82 {
83 // We are not going to write anything in case
84 ssize_t sent = send(httpstate.sockets[httpstate.client_index].descriptor,
85 buf,
86 len,
87 0);
88 CYG_ASSERT(sent == len, "send() did not send out all bytes");
89 return sent;
90 }
91
92 __inline__ ssize_t
93 cyg_httpd_writev(cyg_iovec *bufs, int count)
94 {
95 return writev(httpstate.sockets[httpstate.client_index].descriptor,
96 bufs,
97 count);
98 }
99
100 // The need for chunked transfers arises from the fact that with dinamic
101 // pages it is not always possible to know the packet size upfront, and thus
102 // it is not possible to fill the Content-Length: field in the header.
103 // Today's web browser apparently do not even use 'Content-Length:' because
104 // many sites that generate dinamic frames send a header with a length of 0.
105 // Even though this system works (the browsers probably reads everything that
106 // comes in before the socket is closed and then figure it out) the HTTP
107 // standard _mandates_ the Content-Length: with a correct valie, and whenever
108 // that is not possible, chunked transfers must be used.
109 // This function also handles the case of a chunked frame.
110 //
111 // A chunked transer takes the form of:
112 // -----------------------------------------------------------------------------
113 // cyg_httpd_start_chunked("html");
114 // sprintf(phttpstate->payload, ...);
115 // cyg_httpd_write_chunked(phttpstate->payload,
116 // strlen(phttpstate->payload));
117 // ...
118 // cyg_httpd_end_chunked();
119 // -----------------------------------------------------------------------------
120 ssize_t
121 cyg_httpd_start_chunked(char *extension)
122 {
123 httpstate.status_code = CYG_HTTPD_STATUS_OK;
124 httpstate.mode |= CYG_HTTPD_MODE_TRANSFER_CHUNKED;
125
126 // I am not really sure that this is necessary, but even if it isn't, the
127 // added overhead is not such a big deal. In simple terms, I am not sure
128 // how much I can rely the client to understand that the frame has ended
129 // with the last 5 bytes sent out. In an ideal world, the data '0\r\n\r\n'
130 // should be enough, but several posting I read seem to imply otherwise,
131 // at least with early generation browsers that supported the
132 // "Transfer-Encoding: chunked" mechanism. Things might be getting better
133 // now but I snooped some sites that use the chunked stuff and all of them
134 // with no exception issue a "Connection: close" on chunked frames even
135 // if there is nothing in the HTTP 1.1 spec that requires it.
136 httpstate.mode |= CYG_HTTPD_MODE_CLOSE_CONN;
137
138 // We do not cache the CGI script. In case they are used to display
139 // dynamic data we want them to be executed any every time they are
140 // requested.
141 httpstate.last_modified = -1;
142 httpstate.mime_type = cyg_httpd_find_mime_string(extension);
143 cyg_int32 header_length = cyg_httpd_format_header();
144 ssize_t sent = send(httpstate.sockets[httpstate.client_index].descriptor,
145 httpstate.outbuffer,
146 header_length,
147 0);
148 CYG_ASSERT(sent == header_length, "Did not send out all header bytes");
149 return sent;
150 }
151
152 ssize_t
153 cyg_httpd_write_chunked(char* buf, int len)
154 {
155 char leader[16], trailer[] = {'\r', '\n'};
156
157 cyg_iovec iovec_bufs[] = { {leader, 0}, {buf, 0}, {trailer, 2} };
158 sprintf(leader, "%x\r\n", len);
159 iovec_bufs[0].iov_len = strlen(leader);
160 iovec_bufs[1].iov_len = len;
161 iovec_bufs[2].iov_len = 2;
162 if (httpstate.mode & CYG_HTTPD_SEND_HEADER_ONLY)
163 return (iovec_bufs[0].iov_len + iovec_bufs[1].iov_len +
164 iovec_bufs[2].iov_len);
165 ssize_t sent = cyg_httpd_writev(iovec_bufs, 3);
166 CYG_ASSERT(sent == (iovec_bufs[0].iov_len + iovec_bufs[1].iov_len +
167 iovec_bufs[1].iov_len),
168 "Writev did not send out all bytes");
169 return sent;
170 }
171
172 void
173 cyg_httpd_end_chunked(void)
174 {
175 if (httpstate.mode & CYG_HTTPD_SEND_HEADER_ONLY)
176 return;
177 strcpy(httpstate.outbuffer, "0\r\n\r\n");
178 cyg_httpd_write(httpstate.outbuffer, 5);
179 httpstate.mode &= ~CYG_HTTPD_MODE_TRANSFER_CHUNKED;
180 }
181
182 // This function builds and send out a standard header. It is likely going to
183 // be used by a c language callback function, and thus followed by one or
184 // more calls to cyg_httpd_write(). Unlike cyg_httpd_start_chunked(), this
185 // call requires prior knowledge of the final size of the frame, and the user
186 // is expected to make sure that the total number of bytes (octets) sent out
187 // via 'cyg_httpd_write()' matches the number passed in the len parameter.
188 // Its use it thus more limited, and the more flexible chunked frames should
189 // be used whenever possible.
190 void
191 cyg_httpd_create_std_header(char *extension, int len)
192 {
193 httpstate.status_code = CYG_HTTPD_STATUS_OK;
194 httpstate.mode = 0;
195
196 // We do not want to send out a "Last-Modified:" field for c language
197 // callbacks.
198 httpstate.last_modified = -1;
199 httpstate.mime_type = cyg_httpd_find_mime_string(extension);
200 httpstate.payload_len = len;
201 cyg_int32 header_length = cyg_httpd_format_header();
202 cyg_httpd_write(httpstate.outbuffer, header_length);
203 }
204
205 void
206 cyg_httpd_process_request(cyg_int32 index)
207 {
208 int rc = 1;
209 httpstate.client_index = index;
210 cyg_int32 descr = httpstate.sockets[index].descriptor;
211 fd_set rfds_tmp;
212 FD_ZERO( &rfds_tmp);
213 FD_SET( descr, &rfds_tmp);
214 while (FD_ISSET(descr, &rfds_tmp))
215 {
216 cyg_int32 n = recv(descr, httpstate.inbuffer, CYG_HTTPD_MAXINBUFFER, 0);
217 if (n == 0)
218 {
219 // This is the client that has closed its TX socket, possibly as
220 // a response from a shutdown() initiated by the server. Another
221 // possibility is that the client was closed altogether, in
222 // which case EOFs are sent on each open sockets.
223 #if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 0
224 printf("EOF received on descriptor: %d. Closing it.\n", descr);
225 #endif
226 close(descr);
227 FD_CLR(descr, &httpstate.rfds);
228 httpstate.sockets[index].descriptor = 0;
229 return;
230 }
231
232 if (n < 0)
233 {
234 diag_printf("ERROR reading from socket. read() returned: %d\n", n);
235 return;
236 }
237 httpstate.inbuffer[n] = '\0';
238
239 // Timestamp the socket.
240 httpstate.sockets[index].timestamp = time(NULL);
241
242 // This is where it all happens.
243 cyg_httpd_process_method();
244
245 if (httpstate.mode & CYG_HTTPD_MODE_CLOSE_CONN)
246 // There are 2 cases we can be here:
247 // 1) chunked frames close their connection by default
248 // 2) The client requested the connection be terminated.
249 // In any case, we close the TX pipe, and wait for the client to
250 // send us an EOF on the receive pipe. This is a more graceful way
251 // to handle the closing of the socket, compared to just calling
252 // close() without first asking the opinion of the client, and
253 // running the risk of stray data lingering around.
254 shutdown(descr, SHUT_WR);
255
256 // Now check if we have pipelined frames. We'll set up a
257 // select() to see if more data is waiting to be read. Pipeline is
258 // still not enabled by default in most browsers.
259 struct timeval tv_tmp = {0, 20000};
260 rc = select(descr + 1, &rfds_tmp, NULL, NULL, &tv_tmp);
261 CYG_ASSERT(rc != -1, "select() return -1");
262 if (rc == 0)
263 return;
264 }
265 }
266
267 void
268 cyg_httpd_handle_new_connection(cyg_int32 listener)
269 {
270 cyg_int32 i;
271
272 int fd_client = accept(listener, NULL, NULL);
273 CYG_ASSERT(listener != -1, "accept() failed");
274 if (fd_client == -1)
275 return;
276
277 #if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 0
278 printf("Opening descriptor: %d\n", fd_client);
279 #endif
280 // Timestamp the socket and process the frame immediately, since the accept
281 // guarantees the presence of valid data on the newly opened socket.
282 for (i = 0; i < CYGPKG_NET_MAXSOCKETS; i++)
283 if (httpstate.sockets[i].descriptor == 0)
284 {
285 httpstate.sockets[i].descriptor = fd_client;
286 httpstate.sockets[i].timestamp = time(NULL);
287 cyg_httpd_process_request(i);
288 return;
289 }
290 }
291
292 // This is the "garbage collector" (or better, the "garbage disposer") of
293 // the server. It closes any socket that has been idle for a time period
294 // of CYG_HTTPD_SELECT_TIMEOUT seconds.
295 void
296 cyg_httpd_close_unused_sockets(cyg_int32 listener)
297 {
298 cyg_int32 i;
299
300 #if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 0
301 diag_printf("Garbage collector called\r\n");
302 #endif
303 httpstate.fdmax = listener;
304 for (i = 0; i < CYGPKG_NET_MAXSOCKETS; i++)
305 {
306 if (httpstate.sockets[i].descriptor != 0)
307 {
308 if (time(NULL) - httpstate.sockets[i].timestamp >
309 CYG_HTTPD_SOCKET_IDLE_TIMEOUT)
310 {
311 #if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 0
312 diag_printf("Closing descriptor: %d\n",
313 httpstate.sockets[i].descriptor);
314 #endif
315 shutdown(httpstate.sockets[i].descriptor, SHUT_WR);
316 }
317 else
318 httpstate.fdmax = MAX(httpstate.fdmax,
319 httpstate.sockets[i].descriptor);
320 }
321 }
322 }
323
324 void
325 cyg_httpd_daemon(cyg_addrword_t data)
326 {
327 cyg_int32 rc;
328 init_all_network_interfaces();
329
330 #if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 0
331 #ifdef CYGHWR_NET_DRIVER_ETH0
332 if (eth0_up)
333 {
334 struct bootp* bps = &eth0_bootp_data;
335 diag_printf("ETH0 is up. IP address: %s\n", inet_ntoa(bps->bp_yiaddr));
336 }
337 #endif
338 #endif
339
340 #ifdef CYGOPT_NET_ATHTTPD_USE_CGIBIN_TCL
341 cyg_httpd_init_tcl_interpreter();
342 #endif
343
344 cyg_httpd_initialize();
345
346 // Get the network going. This is benign if the application has
347 // already done this.
348 cyg_int32 listener = socket(AF_INET, SOCK_STREAM, 0);
349 CYG_ASSERT(listener > 0, "Socket create failed");
350 if (listener < 0)
351 return;
352
353 cyg_int32 yes = 1;
354 rc = setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
355 if (rc == -1)
356 return;
357
358 memset(&(httpstate.server_conn), 0, sizeof(struct sockaddr_in));
359 httpstate.server_conn.sin_family = AF_INET;
360 httpstate.server_conn.sin_addr.s_addr = INADDR_ANY;
361 httpstate.server_conn.sin_port = htons(CYGNUM_NET_ATHTTPD_SERVEROPT_PORT);
362 rc = bind(listener,
363 (struct sockaddr *)&httpstate.server_conn,
364 sizeof(struct sockaddr));
365 CYG_ASSERT(rc == 0, "bind() returned error");
366 if (rc != 0)
367 return;
368
369 rc = listen(listener, SOMAXCONN);
370 CYG_ASSERT(rc == 0, "listen() returned error");
371 if (rc != 0)
372 return;
373
374 diag_printf("Web server Started and listening...\n");
375 cyg_int32 i;
376 for (i = 0; i < CYGNUM_FILEIO_NFILE; i++)
377 {
378 httpstate.sockets[i].descriptor = 0;
379 httpstate.sockets[i].timestamp = (time_t)0;
380 }
381
382 FD_ZERO(&httpstate.rfds);
383 httpstate.fdmax = listener;
384 while (1)
385 {
386 FD_SET(listener, &httpstate.rfds);
387 struct timeval tv = {CYG_HTTPD_SOCKET_IDLE_TIMEOUT, 0};
388 rc = select(httpstate.fdmax + 1, &httpstate.rfds, NULL, NULL, &tv);
389 if (rc > 0)
390 {
391 if (FD_ISSET(listener, &httpstate.rfds))
392 // If the request is from the listener socket, then
393 // this must be a new connection.
394 cyg_httpd_handle_new_connection(listener);
395
396 httpstate.fdmax = listener;
397 for (i = 0; i < CYGPKG_NET_MAXSOCKETS; i ++)
398 {
399 cyg_int32 descr = httpstate.sockets[i].descriptor;
400 if (descr != 0)
401 {
402 // If the descriptor is set in the descriptor list, we
403 // service it. Otherwise, we add it to the descriptor list
404 // to listen for. The rfds list gets rewritten each time
405 // select() is called and after the call it contains only
406 // the descriptors that need be serviced. Before calling
407 // select() again we must repopulate the list with all the
408 // descriptors that must be listened for.
409 if (FD_ISSET(descr, &httpstate.rfds))
410 cyg_httpd_process_request(i);
411 else
412 FD_SET(descr, &httpstate.rfds);
413 httpstate.fdmax = MAX(httpstate.fdmax, descr);
414 }
415 }
416 }
417 else if (rc == 0)
418 {
419 cyg_httpd_close_unused_sockets(listener);
420 }
421 else
422 {
423 perror("error");
424 cyg_int8 *ptr = (cyg_int8*)&httpstate.rfds;
425 printf("rfds: %x %x %x %x\n", ptr[0], ptr[1], ptr[2], ptr[3] );
426 for (i = 0; i < CYGPKG_NET_MAXSOCKETS; i++)
427 if (httpstate.sockets[i].descriptor != 0)
428 diag_printf("Socket in list: %d\n",
429 httpstate.sockets[i].descriptor);
430 CYG_ASSERT(rc != -1, "Error during select()");
431 }
432 }
433 }
434
435 void
436 cyg_httpd_start(void)
437 {
438 if (cyg_httpd_initialized)
439 return;
440 cyg_httpd_initialized = 1;
441
442 cyg_thread_create(CYGNUM_NET_ATHTTPD_THREADOPT_PRIORITY,
443 cyg_httpd_daemon,
444 (cyg_addrword_t)0,
445 "HTTPD Thread",
446 (void *)cyg_httpd_thread_stack,
447 CYG_HTTPD_DAEMON_STACK_SIZE,
448 &cyg_httpd_thread_handle,
449 &cyg_httpd_thread_object);
450 cyg_thread_resume(cyg_httpd_thread_handle);
451 }
452
453
454