OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [rtos/] [ecos-3.0/] [packages/] [net/] [athttpd/] [current/] [src/] [socket.c] - Blame information for rev 867

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 786 skrzyp
/* =================================================================
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 System.
11
 * Copyright (C) 2005 Free Software Foundation, Inc.
12
 *
13
 * eCos is free software; you can redistribute it and/or modify it under
14
 * the terms of the GNU General Public License as published by the Free
15
 * Software Foundation; either version 2 or (at your option) any later
16
 * version.
17
 *
18
 * eCos is distributed in the hope that it will be useful, but WITHOUT
19
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
21
 * for more details.
22
 *
23
 * You should have received a copy of the GNU General Public License
24
 * along with eCos; if not, write to the Free Software Foundation, Inc.,
25
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
26
 *
27
 * As a special exception, if other files instantiate templates or use
28
 * macros or inline functions from this file, or you compile this file
29
 * and link it with other works to produce a work based on this file,
30
 * this file does not by itself cause the resulting work to be covered by
31
 * the GNU General Public License. However the source code for this file
32
 * must still be made available in accordance with section (3) of the GNU
33
 * General Public License v2.
34
 *
35
 * This exception does not invalidate any other reasons why a work based
36
 * on this file might be covered by the GNU General Public License.
37
 * -------------------------------------------
38
 * ####ECOSGPLCOPYRIGHTEND####
39
 * =================================================================
40
 * #####DESCRIPTIONBEGIN####
41
 *
42
 *  Author(s):    Anthony Tonizzo (atonizzo@gmail.com)
43
 *  Contributors: Sergei Gavrikov (w3sg@SoftHome.net),
44
 *                Lars Povlsen    (lpovlsen@vitesse.com)
45
 *  Date:         2006-06-12
46
 *  Purpose:
47
 *  Description:
48
 *
49
 * ####DESCRIPTIONEND####
50
 *
51
 * =================================================================
52
 */
53
#include <pkgconf/hal.h>
54
#include <pkgconf/kernel.h>
55
#include <cyg/kernel/kapi.h>           // Kernel API.
56
#include <cyg/kernel/ktypes.h>         // base kernel types.
57
#include <cyg/infra/diag.h>            // For diagnostic printing.
58
#include <network.h>
59
#include <sys/uio.h>
60
#include <fcntl.h>
61
#include <stdio.h>                     // sprintf().
62
#include <time.h>                      // sprintf().
63
 
64
#include <cyg/athttpd/http.h>
65
#include <cyg/athttpd/socket.h>
66
#include <cyg/athttpd/cgi.h>
67
 
68
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
69
#define CYG_HTTPD_DAEMON_STACK_SIZE (CYGNUM_HAL_STACK_SIZE_MINIMUM + \
70
                                          CYGNUM_NET_ATHTTPD_THREADOPT_STACKSIZE)
71
static cyg_int32 cyg_httpd_initialized = 0;
72
cyg_thread   cyg_httpd_thread_object;
73
cyg_handle_t cyg_httpd_thread_handle;
74
cyg_uint8    cyg_httpd_thread_stack[CYG_HTTPD_DAEMON_STACK_SIZE]
75
                                       __attribute__((__aligned__ (16)));
76
CYG_HTTPD_STATE httpstate;
77
 
78
__inline__ ssize_t
79
cyg_httpd_write(char* buf, int buf_len)
80
{
81
    // We are not going to write anything in case
82
    ssize_t sent = send(httpstate.sockets[httpstate.client_index].descriptor,
83
                        buf,
84
                        buf_len,
85
                        0);
86
    return sent;
87
}
88
 
89
__inline__ ssize_t
90
cyg_httpd_writev(cyg_iovec *iovec_bufs, int count)
91
{
92
    int i;
93
    ssize_t sent = writev(httpstate.sockets[httpstate.client_index].descriptor,
94
                          iovec_bufs,
95
                          count);
96
    ssize_t buf_len = 0;
97
    for (i = 0; i < count; i++)
98
        buf_len += iovec_bufs[i].iov_len;
99
#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
100
    if (sent != buf_len)
101
        diag_printf("writev() did not send out all bytes (%ld of %ld)\n",
102
                    sent,
103
                    buf_len);
104
#endif    
105
    return sent;
106
}
107
 
108
// The need for chunked transfers arises from the fact that with persistent
109
//  connections it is not always easy to tell when a packet end. Also, with
110
//  dynamic pages it is not always possible to know the packet size upfront,
111
//  and thus the value of the 'Content-Length:' field in the header is not
112
//  known upfront.
113
// Today's web browser use 'Content-Length:' when present in the header and 
114
//  when not present they read everything that comes in up to the last 2 \r\n
115
//  and then figure it out. The HTTP standard _mandates_ 'Content-Length:' to
116
//  be present in the header with a correct value, and whenever that is not
117
//  possible, chunked transfers must be used.
118
//
119
// A chunked transer takes the form of:
120
// -----------------------------------------------------------------------------
121
//    cyg_httpd_start_chunked("html");
122
//    sprintf(phttpstate->payload, ...);             
123
//    cyg_httpd_write_chunked(phttpstate->payload, strlen(phttpstate->payload));
124
//    ...                         
125
//    cyg_httpd_end_chunked();
126
// -----------------------------------------------------------------------------
127
ssize_t
128
cyg_httpd_start_chunked(char *extension)
129
{
130
    httpstate.status_code = CYG_HTTPD_STATUS_OK;
131
 
132
#if defined(CYGOPT_NET_ATHTTPD_CLOSE_CHUNKED_CONNECTIONS)
133
    // I am not really sure that this is necessary, but even if it isn't, the
134
    //  added overhead is not such a big deal. In simple terms, I am not sure 
135
    //  how much I can rely on the client to understand that the frame has ended 
136
    //  with the last 5 bytes sent out. In an ideal world, the data '0\r\n\r\n'
137
    //  should be enough, but several posting on the subject I read seem to
138
    //  imply otherwise, at least with early generation browsers that supported
139
    //  the "Transfer-Encoding: chunked" mechanism. Things might be getting 
140
    //  better now but I snooped some sites that use the chunked stuff (Yahoo!
141
    //  for one) and all of them with no exception issue a "Connection: close" 
142
    //  on chunked frames even if there is nothing in the HTTP 1.1 spec that
143
    //  requires it.
144
    httpstate.mode |= CYG_HTTPD_MODE_CLOSE_CONN;
145
#endif
146
 
147
    // We do not cache chunked frames. In case they are used to display dynamic
148
    //  data we want them to be executed every time they are requested.
149
    httpstate.mode |=
150
              (CYG_HTTPD_MODE_TRANSFER_CHUNKED | CYG_HTTPD_MODE_NO_CACHE);
151
 
152
    httpstate.last_modified = -1;
153
    httpstate.mime_type = cyg_httpd_find_mime_string(extension);
154
    cyg_int32 header_length = cyg_httpd_format_header();
155
    return cyg_httpd_write(httpstate.outbuffer, header_length);
156
}
157
 
158
ssize_t
159
cyg_httpd_write_chunked(char* buf, int len)
160
{
161
    if (len == 0)
162
         return 0;
163
 
164
    char leader[16], trailer[] = {'\r', '\n'};
165
    cyg_iovec iovec_bufs[] = { {leader, 0}, {buf, len}, {trailer, 2} };
166
    iovec_bufs[0].iov_len = sprintf(leader, "%x\r\n", len);
167
    if (httpstate.mode & CYG_HTTPD_MODE_SEND_HEADER_ONLY)
168
        return (iovec_bufs[0].iov_len + len + 2);
169
    return cyg_httpd_writev(iovec_bufs, 3);
170
}
171
 
172
void
173
cyg_httpd_end_chunked(void)
174
{
175
    httpstate.mode &= ~CYG_HTTPD_MODE_TRANSFER_CHUNKED;
176
    if ((httpstate.mode & CYG_HTTPD_MODE_SEND_HEADER_ONLY) != 0)
177
        return;
178
    strcpy(httpstate.outbuffer, "0\r\n\r\n");
179
    cyg_httpd_write(httpstate.outbuffer, 5);
180
}
181
 
182
// This function builds and sends 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 (browsers
186
//  _will_trust_ the "Content-Length:" field when present!), and the user 
187
//  is expected to make sure that the total number of bytes (octets) sent out
188
//  via 'cyg_httpd_write()' matches the number passed in the len parameter.
189
// Its use is thus more limited, and the more flexible chunked frames should 
190
//  be used whenever possible.
191
void
192
cyg_httpd_create_std_header(char *extension, int len)
193
{
194
    httpstate.status_code = CYG_HTTPD_STATUS_OK;
195
    httpstate.mode |= CYG_HTTPD_MODE_NO_CACHE;
196
 
197
    // We do not want to send out a "Last-Modified:" field for c language
198
    //  callbacks.
199
    httpstate.last_modified = -1;
200
    httpstate.mime_type = cyg_httpd_find_mime_string(extension);
201
    httpstate.payload_len = len;
202
    cyg_int32 header_length = cyg_httpd_format_header();
203
    cyg_httpd_write(httpstate.outbuffer, header_length);
204
}
205
 
206
void
207
cyg_httpd_process_request(cyg_int32 index)
208
{
209
    httpstate.client_index = index;
210
    cyg_int32 descr = httpstate.sockets[index].descriptor;
211
 
212
    // By placing a terminating '\0' not only we have a safe stopper point
213
    //  for our parsing, but also we can detect if we have a split header.
214
    // Since headers always end with an extra '\r\n', if we find a '\0'
215
    //  before the terminator than we can safely assume that the header has
216
    //  not been received completely and more is following (i.e. split headers.)
217
    httpstate.inbuffer[0] = '\0';
218
    httpstate.inbuffer_len = 0;
219
 
220
    cyg_bool done = false;
221
    do
222
    {
223
        // At this point we know we have data pending because the corresponding
224
        //  bit in the fd_set structure was set.
225
        int len = recv(descr,
226
                       httpstate.inbuffer + httpstate.inbuffer_len,
227
                       CYG_HTTPD_MAXINBUFFER - httpstate.inbuffer_len,
228
                       0);
229
        if (len == 0)
230
        {
231
            // This is the client that has closed its TX socket, possibly as
232
            //  a response from a shutdown() initiated by the server. Another
233
            //  possibility is that the client was closed altogether, in
234
            //  which case the client sent EOFs on each open sockets before 
235
            //  dying.
236
            close(descr);
237
            FD_CLR(descr, &httpstate.rfds);
238
            httpstate.sockets[index].descriptor = 0;
239
#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 0
240
            printf("EOF received on descriptor: %d. Closing it.\n", descr);
241
#endif    
242
            return;
243
        }
244
 
245
        if (len < 0)
246
        {
247
            // There was an error reading from this socket. Play it safe and
248
            //  close it. This will force the client to generate a shutdown
249
            //  and we will read a len = 0 the next time around.
250
            shutdown(descr, SHUT_WR);
251
#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 0
252
            diag_printf("ERROR reading from socket. read() returned: %d\n",
253
                        httpstate.inbuffer_len);
254
#endif    
255
            return;
256
        }
257
 
258
        httpstate.inbuffer[httpstate.inbuffer_len + len] = '\0';
259
 
260
        // It is always possible to receive split headers, in which case a
261
        //  header is only partially sent on one packet, with the rest on
262
        //  following packets. We can tell when a full packet is in the buffer
263
        //  by scanning for a header terminator ('\r\n\r\n'). Be smart and
264
        //  scan only the data received in the last read() operation, and not
265
        //  the full buffer each time.
266
        httpstate.request_end =
267
               strstr(&httpstate.inbuffer[httpstate.inbuffer_len], "\r\n\r\n");
268
        httpstate.inbuffer_len += len;
269
 
270
        // Go through all the requests that were received in this packet.
271
        while (httpstate.request_end != 0)
272
        {
273
            httpstate.request_end += 4; // Include the terminator.
274
 
275
            // Timestamp the socket. 
276
            httpstate.sockets[index].timestamp = time(NULL);
277
 
278
            // This is where it all happens.
279
            cyg_httpd_process_method();
280
 
281
            if (httpstate.mode & CYG_HTTPD_MODE_CLOSE_CONN)
282
            {
283
                // There are 2 cases we can be here:
284
                // 1) chunked frames close their connection by default
285
                // 2) The client requested the connection be terminated with a
286
                //     "Connection: close" in the header
287
                // In any case, we close the TX pipe and wait for the client to
288
                //  send us an EOF on the receive pipe. This is a more graceful
289
                //  way to handle the closing of the socket, compared to just
290
                //  calling close() without first asking the opinion of the
291
                //  client, and  running the risk of stray data lingering 
292
                //  around.
293
                shutdown(descr, SHUT_WR);
294
            }
295
 
296
            // Move back the next request (if any) to the beginning of inbuffer.
297
            //  This way we avoid inching towards the end of inbuffer with
298
            //  consecutive requests.
299
            strcpy(httpstate.inbuffer, httpstate.request_end);
300
            httpstate.inbuffer_len -= (int)(httpstate.request_end -
301
                                                       httpstate.inbuffer);
302
 
303
            // If there is no data left over we are done processing all
304
            //  requests.
305
            if (httpstate.inbuffer_len == 0)
306
            {
307
                done = true;
308
                break;
309
            }
310
 
311
            // Any other fully formed request pending?                                           
312
            httpstate.request_end = strstr(httpstate.inbuffer, "\r\n\r\n");
313
        }
314
    }
315
    while (done == false);
316
}
317
 
318
void
319
cyg_httpd_handle_new_connection(cyg_int32 listener)
320
{
321
    cyg_int32 i;
322
 
323
    int fd_client = accept(listener, NULL, NULL);
324
    CYG_ASSERT(listener != -1, "accept() failed");
325
    if (fd_client == -1)
326
        return;
327
 
328
#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 0
329
    diag_printf("Opening descriptor: %d\n", fd_client);
330
#endif    
331
    // Timestamp the socket and process the frame immediately, since the accept
332
    //  guarantees the presence of valid data on the newly opened socket.
333
    for (i = 0; i < CYGPKG_NET_MAXSOCKETS; i++)
334
        if (httpstate.sockets[i].descriptor == 0)
335
        {
336
            httpstate.sockets[i].descriptor = fd_client;
337
            httpstate.sockets[i].timestamp  = time(NULL);
338
            cyg_httpd_process_request(i);
339
            return;
340
        }
341
}
342
 
343
// This is the "garbage collector" (or better, the "garbage disposer") of
344
//  the server. It closes any socket that has been idle for a time period
345
//  of CYG_HTTPD_SELECT_TIMEOUT seconds.
346
void
347
cyg_httpd_close_unused_sockets(cyg_int32 listener)
348
{
349
    cyg_int32 i;
350
 
351
#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 0
352
    diag_printf("Garbage collector called\r\n");
353
#endif    
354
    httpstate.fdmax = listener;
355
    for (i = 0; i < CYGPKG_NET_MAXSOCKETS; i++)
356
    {
357
        if (httpstate.sockets[i].descriptor != 0)
358
        {
359
            if (time(NULL) - httpstate.sockets[i].timestamp >
360
                                          CYG_HTTPD_SOCKET_IDLE_TIMEOUT)
361
            {
362
#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 0
363
                diag_printf("Closing descriptor: %d\n",
364
                            httpstate.sockets[i].descriptor);
365
#endif    
366
                shutdown(httpstate.sockets[i].descriptor, SHUT_WR);
367
            }
368
            else
369
                httpstate.fdmax = MAX(httpstate.fdmax,
370
                                      httpstate.sockets[i].descriptor);
371
        }
372
    }
373
}
374
 
375
void
376
cyg_httpd_daemon(cyg_addrword_t data)
377
{
378
    cyg_int32 rc;
379
    init_all_network_interfaces();
380
 
381
#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 0
382
#ifdef CYGHWR_NET_DRIVER_ETH0
383
    if (eth0_up)
384
    {
385
        struct bootp* bps = &eth0_bootp_data;
386
        diag_printf("ETH0 is up. IP address: %s\n", inet_ntoa(bps->bp_yiaddr));
387
    }
388
#endif
389
#endif
390
 
391
#ifdef CYGOPT_NET_ATHTTPD_USE_CGIBIN_TCL
392
    cyg_httpd_init_tcl_interpreter();
393
#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 0
394
    diag_printf("Tcl interpreter has been initialized...\n");
395
#endif
396
#endif    
397
 
398
    cyg_httpd_initialize();
399
 
400
    // Get the network going. This is benign if the application has
401
    //  already done this.
402
    cyg_int32 listener = socket(AF_INET, SOCK_STREAM, 0);
403
    CYG_ASSERT(listener > 0, "Socket create failed");
404
    if (listener < 0)
405
        return;
406
 
407
    cyg_int32 yes = 1;
408
    rc = setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
409
    if (rc == -1)
410
        return;
411
 
412
    memset(&(httpstate.server_conn), 0, sizeof(struct sockaddr_in));
413
    httpstate.server_conn.sin_family = AF_INET;
414
    httpstate.server_conn.sin_addr.s_addr = INADDR_ANY;
415
    httpstate.server_conn.sin_port = htons(CYGNUM_NET_ATHTTPD_SERVEROPT_PORT);
416
    rc = bind(listener,
417
              (struct sockaddr *)&httpstate.server_conn,
418
              sizeof(struct sockaddr));
419
    CYG_ASSERT(rc == 0, "bind() returned error");
420
    if (rc != 0)
421
        return;
422
 
423
    rc = listen(listener, SOMAXCONN);
424
    CYG_ASSERT(rc == 0, "listen() returned error");
425
    if (rc != 0)
426
        return;
427
 
428
#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 0
429
    diag_printf("Web server Started and listening...\n");
430
#endif
431
    cyg_int32 i;
432
    for (i = 0; i < CYGPKG_NET_MAXSOCKETS; i++)
433
    {
434
        httpstate.sockets[i].descriptor  = 0;
435
        httpstate.sockets[i].timestamp   = (time_t)0;
436
    }
437
 
438
    FD_ZERO(&httpstate.rfds);
439
    httpstate.fdmax = listener;
440
    while (1)
441
    {
442
        // The listener is always added to the select() sensitivity list.
443
        FD_SET(listener, &httpstate.rfds);
444
        struct timeval tv = {CYG_HTTPD_SOCKET_IDLE_TIMEOUT, 0};
445
        rc = select(httpstate.fdmax + 1, &httpstate.rfds, NULL, NULL, &tv);
446
        if (rc > 0)
447
        {
448
            if (FD_ISSET(listener, &httpstate.rfds))
449
                // If the request is from the listener socket, then 
450
                //  this must be a new connection.
451
                cyg_httpd_handle_new_connection(listener);
452
 
453
            httpstate.fdmax = listener;
454
 
455
            // The sensitivity list returned by select() can have multiple
456
            //  socket descriptors that need service. Loop through the whole
457
            //  descriptor list to see if one or more need to be served.
458
            for (i = 0; i < CYGPKG_NET_MAXSOCKETS; i ++)
459
            {
460
                cyg_int32 descr = httpstate.sockets[i].descriptor;
461
                if (descr != 0)
462
                {
463
                    // If the descriptor is set in the descriptor list, we
464
                    //  service it. Otherwise, we add it to the descriptor list
465
                    //  to listen for. The rfds list gets rewritten each time
466
                    //  select() is called and after the call it contains only
467
                    //  the descriptors that need be serviced. Before calling
468
                    //  select() again we must repopulate the list with all the
469
                    //  descriptors that must be listened for.
470
                    if (FD_ISSET(descr, &httpstate.rfds))
471
                        cyg_httpd_process_request(i);
472
                    else
473
                        FD_SET(descr, &httpstate.rfds);
474
                    if (httpstate.sockets[i].descriptor != 0)
475
                        httpstate.fdmax = MAX(httpstate.fdmax, descr);
476
                }
477
            }
478
        }
479
        else if (rc == 0)
480
        {
481
            cyg_httpd_close_unused_sockets(listener);
482
        }
483
        else
484
        {
485
#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 0
486
            cyg_int8 *ptr = (cyg_int8*)&httpstate.rfds;
487
            diag_printf("rfds: %x %x %x %x\n", ptr[0], ptr[1], ptr[2], ptr[3] );
488
            for (i = 0; i < CYGPKG_NET_MAXSOCKETS; i++)
489
                if (httpstate.sockets[i].descriptor != 0)
490
                     diag_printf("Socket in list: %d\n",
491
                                 httpstate.sockets[i].descriptor);
492
#endif                                 
493
            CYG_ASSERT(rc != -1, "Error during select()");
494
        }
495
    }
496
}
497
 
498
void
499
cyg_httpd_start(void)
500
{
501
    if (cyg_httpd_initialized)
502
        return;
503
    cyg_httpd_initialized = 1;
504
 
505
    cyg_thread_create(CYGNUM_NET_ATHTTPD_THREADOPT_PRIORITY,
506
                      cyg_httpd_daemon,
507
                      (cyg_addrword_t)0,
508
                      "HTTPD Thread",
509
                      (void *)cyg_httpd_thread_stack,
510
                      CYG_HTTPD_DAEMON_STACK_SIZE,
511
                      &cyg_httpd_thread_handle,
512
                      &cyg_httpd_thread_object);
513
    cyg_thread_resume(cyg_httpd_thread_handle);
514
}
515
 
516
 
517
 

powered by: WebSVN 2.1.0

© copyright 1999-2025 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.