Mercurial > ecos
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 = ð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 |
