📄 socket.c
字号:
/* =================================================================
*
* socket.c
*
* Opens socket and starts the daemon.
*
* =================================================================
* ####ECOSGPLCOPYRIGHTBEGIN####
* -------------------------------------------
* This file is part of eCos, the Embedded Configurable Operating
* System.
* Copyright (C) 2005 eCosCentric Ltd.
*
* eCos is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 or (at your option)
* any later version.
*
* eCos is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with eCos; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*
* As a special exception, if other files instantiate templates or
* use macros or inline functions from this file, or you compile this
* file and link it with other works to produce a work based on this
* file, this file does not by itself cause the resulting work to be
* covered by the GNU General Public License. However the source code
* for this file must still be made available in accordance with
* section (3) of the GNU General Public License.
*
* This exception does not invalidate any other reasons why a work
* based on this file might be covered by the GNU General Public
* License.
*
* -------------------------------------------
* ####ECOSGPLCOPYRIGHTEND####
* =================================================================
* #####DESCRIPTIONBEGIN####
*
* Author(s): Anthony Tonizzo (atonizzo@gmail.com)
* Contributors: Sergei Gavrikov (w3sg@SoftHome.net),
* Lars Povlsen (lpovlsen@vitesse.com)
* Date: 2006-06-12
* Purpose:
* Description:
*
* ####DESCRIPTIONEND####
*
* =================================================================
*/
#include <pkgconf/hal.h>
#include <pkgconf/kernel.h>
#include <cyg/kernel/kapi.h> // Kernel API.
#include <cyg/kernel/ktypes.h> // base kernel types.
#include <cyg/infra/diag.h> // For diagnostic printing.
#include <network.h>
#include <sys/uio.h>
#include <fcntl.h>
#include <stdio.h> // sprintf().
#include <time.h> // sprintf().
#include <cyg/athttpd/http.h>
#include <cyg/athttpd/socket.h>
#include <cyg/athttpd/cgi.h>
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
#define CYG_HTTPD_DAEMON_STACK_SIZE (CYGNUM_HAL_STACK_SIZE_MINIMUM + \
CYGNUM_NET_ATHTTPD_THREADOPT_STACKSIZE)
static cyg_int32 cyg_httpd_initialized = 0;
cyg_thread cyg_httpd_thread_object;
cyg_handle_t cyg_httpd_thread_handle;
cyg_uint8 cyg_httpd_thread_stack[CYG_HTTPD_DAEMON_STACK_SIZE]
__attribute__((__aligned__ (16)));
CYG_HTTPD_STATE httpstate;
__inline__ ssize_t
cyg_httpd_write(char* buf, int buf_len)
{
// We are not going to write anything in case
ssize_t sent = send(httpstate.sockets[httpstate.client_index].descriptor,
buf,
buf_len,
0);
return sent;
}
__inline__ ssize_t
cyg_httpd_writev(cyg_iovec *iovec_bufs, int count)
{
int i;
ssize_t sent = writev(httpstate.sockets[httpstate.client_index].descriptor,
iovec_bufs,
count);
ssize_t buf_len = 0;
for (i = 0; i < count; i++)
buf_len += iovec_bufs[i].iov_len;
#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
if (sent != buf_len)
diag_printf("writev() did not send out all bytes (%ld of %ld)\n",
sent,
buf_len);
#endif
return sent;
}
// The need for chunked transfers arises from the fact that with dinamic
// pages it is not always possible to know the packet size upfront, and thus
// it is not possible to fill the 'Content-Length:' field in the header.
// Today's web browser use 'Content-Length:' when present in the header and
// when not present they read everything that comes in up to the last 2 \r\n
// and then figure it out. The HTTP standard _mandates_ 'Content-Length:' to
// be present in the header with a correct value, and whenever that is not
// possible, chunked transfers must be used.
//
// A chunked transer takes the form of:
// -----------------------------------------------------------------------------
// cyg_httpd_start_chunked("html");
// sprintf(phttpstate->payload, ...);
// cyg_httpd_write_chunked(phttpstate->payload,
// strlen(phttpstate->payload));
// ...
// cyg_httpd_end_chunked();
// -----------------------------------------------------------------------------
ssize_t
cyg_httpd_start_chunked(char *extension)
{
httpstate.status_code = CYG_HTTPD_STATUS_OK;
#if defined(CYGOPT_NET_ATHTTPD_CLOSE_CHUNKED_CONNECTIONS)
// I am not really sure that this is necessary, but even if it isn't, the
// added overhead is not such a big deal. In simple terms, I am not sure
// how much I can rely on the client to understand that the frame has ended
// with the last 5 bytes sent out. In an ideal world, the data '0\r\n\r\n'
// should be enough, but several posting on the subject I read seem to
// imply otherwise, at least with early generation browsers that supported
// the "Transfer-Encoding: chunked" mechanism. Things might be getting
// better now but I snooped some sites that use the chunked stuff (Yahoo!
// for one) and all of them with no exception issue a "Connection: close"
// on chunked frames even if there is nothing in the HTTP 1.1 spec that
// requires it.
httpstate.mode |= CYG_HTTPD_MODE_CLOSE_CONN;
#endif
// We do not cache chunked frames. In case they are used to display dynamic
// data we want them to be executed any every time they are requested.
httpstate.mode |=
(CYG_HTTPD_MODE_TRANSFER_CHUNKED | CYG_HTTPD_MODE_NO_CACHE);
httpstate.last_modified = -1;
httpstate.mime_type = cyg_httpd_find_mime_string(extension);
cyg_int32 header_length = cyg_httpd_format_header();
return cyg_httpd_write(httpstate.outbuffer, header_length);
}
ssize_t
cyg_httpd_write_chunked(char* buf, int len)
{
char leader[16], trailer[] = {'\r', '\n'};
cyg_iovec iovec_bufs[] = { {leader, 0}, {buf, 0}, {trailer, 2} };
sprintf(leader, "%x\r\n", len);
iovec_bufs[0].iov_len = strlen(leader);
iovec_bufs[1].iov_len = len;
iovec_bufs[2].iov_len = 2;
if (httpstate.mode & CYG_HTTPD_MODE_SEND_HEADER_ONLY)
return (iovec_bufs[0].iov_len + iovec_bufs[1].iov_len +
iovec_bufs[2].iov_len);
return cyg_httpd_writev(iovec_bufs, 3);
}
void
cyg_httpd_end_chunked(void)
{
if (httpstate.mode & CYG_HTTPD_MODE_SEND_HEADER_ONLY)
return;
strcpy(httpstate.outbuffer, "0\r\n\r\n");
cyg_httpd_write(httpstate.outbuffer, 5);
httpstate.mode &= ~CYG_HTTPD_MODE_TRANSFER_CHUNKED;
}
// This function builds and send out a standard header. It is likely going to
// be used by a c language callback function, and thus followed by one or
// more calls to cyg_httpd_write(). Unlike cyg_httpd_start_chunked(), this
// call requires prior knowledge of the final size of the frame (browsers
// _will_trust_ the "Content-Length:" field when present!), and the user
// is expected to make sure that the total number of bytes (octets) sent out
// via 'cyg_httpd_write()' matches the number passed in the len parameter.
// Its use is thus more limited, and the more flexible chunked frames should
// be used whenever possible.
void
cyg_httpd_create_std_header(char *extension, int len)
{
httpstate.status_code = CYG_HTTPD_STATUS_OK;
httpstate.mode |= CYG_HTTPD_MODE_NO_CACHE;
// We do not want to send out a "Last-Modified:" field for c language
// callbacks.
httpstate.last_modified = -1;
httpstate.mime_type = cyg_httpd_find_mime_string(extension);
httpstate.payload_len = len;
cyg_int32 header_length = cyg_httpd_format_header();
cyg_httpd_write(httpstate.outbuffer, header_length);
}
void
cyg_httpd_process_request(cyg_int32 index)
{
httpstate.client_index = index;
cyg_int32 descr = httpstate.sockets[index].descriptor;
// By placing a terminating '\0' not only we have a safe stopper point
// for our parsing, but also we can detect if we have a split header.
// Since headers always end with an extra '\r\n', if we find a '\0'
// before the terminator than we can safely assume that the header has
// not been received completely and more is following (i.e. split headers.)
httpstate.inbuffer[0] = '\0';
httpstate.inbuffer_len = 0;
while ((strstr(httpstate.inbuffer, "\r\n\r\n") == 0) &&
(strstr(httpstate.inbuffer, "\n\n") == 0))
{
int len = recv(descr,
httpstate.inbuffer + httpstate.inbuffer_len,
CYG_HTTPD_MAXINBUFFER - httpstate.inbuffer_len,
0);
if (len == 0)
{
// This is the client that has closed its TX socket, possibly as
// a response from a shutdown() initiated by the server. Another
// possibility is that the client was closed altogether, in
// which case the client sent EOFs on each open sockets before
// dying.
close(descr);
FD_CLR(descr, &httpstate.rfds);
httpstate.sockets[index].descriptor = 0;
#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 0
printf("EOF received on descriptor: %d. Closing it.\n", descr);
#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -