|
2250
|
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 = ð0_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 |