📄 httpserv.c
字号:
/*
* Copyright (C) 2001-2007 by egnite Software GmbH. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE
* SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* For additional information see http://www.ethernut.de/
*
*/
/*!
* \file httpserv.c
* \brief HTTP interface.
*
* \verbatim
*
* $Log$
*
* \endverbatim
*/
#ifndef MY_FSDEV
#define MY_FSDEV devUrom
#endif
#ifdef MY_FSDEV_NAME
#define MY_HTTPROOT MY_FSDEV_NAME ":/"
#endif
#include <cfg/os.h>
#include <cfg/memory.h>
#include <stdlib.h>
#include <string.h>
#include <io.h>
#include <fcntl.h>
#include <time.h>
#include <dev/board.h>
#include <dev/urom.h>
#include <sys/thread.h>
#include <sys/timer.h>
#include <sys/heap.h>
#include <sys/socket.h>
#include <pro/httpd.h>
#include "tlv320dac.h"
#include "httpserv.h"
#ifndef HTTPD_THREAD_STACK
#define HTTPD_THREAD_STACK 2048
#endif
#ifdef USE_HTTPSERVER
/*! \brief Dummy gain.
*
* Used to determine volume field that had been left empty.
* We can't use zero.
*/
#define EMPTY_GAIN 999
extern void *__heap_start;
extern void *__etext;
int h_timevalid;
static char *html_mt = "text/html";
/*!
* \brief Radio Info CGI.
*
* Dynamically creates a page that displays our current status.
* It will be automatically refreshed every 10 seconds. Thus,
* let's keep it simple
*/
static int CgiInfo(FILE * stream, REQUEST * req)
{
/* These useful API calls create a HTTP response for us. */
NutHttpSendHeaderTop(stream, req, 200, "Ok");
NutHttpSendHeaderBot(stream, html_mt, -1);
/* Send HTML header. */
fputs("<HTML><HEAD><TITLE>Internet Radio Information</TITLE>" /* */
"<meta http-equiv=\"refresh\" content=\"10; URL=info.cgi\">" /* */
"</HEAD><BODY>\r\n" /* */
"<h1>Internet Radio Status</h1>" /* */
, stream);
/* If we received date and time from a time server, display it. */
if (h_timevalid) {
time_t now = time(0);
struct _tm *lot = localtime(&now);
fprintf(stream, "Date = %02u.%02u.%u<br>\r\n", lot->tm_mday, lot->tm_mon + 1, 1900 + lot->tm_year);
fprintf(stream, "Time = %02u:%02u:%02u<br>\r\n", lot->tm_hour, lot->tm_min, lot->tm_sec);
}
/* Display our clock settings. */
fprintf(stream, "<br>CPU Clock = %lu kHz<br>\r\n", NutGetCpuClock() / 1000);
fprintf(stream, "Master Clock = %lu kHz<br>\r\n", At91GetMasterClock() / 1000);
/* Display our memory usage. */
fprintf(stream, "<br>Code size incl. Web Content = %u kBytes<br>\r\n", ((u_int)(&__etext) - NUTMEM_START) / 1024);
fprintf(stream, "Static data size = %u kBytes<br>\r\n", (u_int)(&__heap_start - &__etext) / 1024);
fprintf(stream, "Total memory used = %u kBytes<br>\r\n", (u_int)(NUTMEM_SIZE - NutHeapAvailable()) / 1024);
fflush(stream);
if (radio.rc_sip == NULL) {
fputs("<br><br><b>No station connected<br>\r\n", stream);
}
else {
fprintf(stream, "<h2>Radio Station</h2>\r\n");
fprintf(stream, "Name = %s<br>\r\n", radio.rc_sip->si_name);
fprintf(stream, "Genre = %s<br>\r\n", radio.rc_sip->si_genre);
fprintf(stream, "Bitrate = %u kBit<br>\r\n", radio.rc_sip->si_bitrate);
fflush(stream);
if (radio.rc_rip) {
SHOUTCASTINFO *sci = (SHOUTCASTINFO *) radio.rc_rip->ri_bcast;
fprintf(stream, "<h2>SHOUTcast Stream</h2>\r\n");
fprintf(stream, "Buffer contents = %u Bytes<br>\r\n", radio.rc_rip->ri_avail);
if (sci) {
fprintf(stream, "Title = %s<br>\r\n", sci->sci_metatitle);
fprintf(stream, "URL = %s<br>\r\n", sci->sci_metaurl);
}
fprintf(stream, "Metadata interval = %lu Bytes<br>\r\n", sci->sci_metaint);
fflush(stream);
if (radio.rc_pip) {
MP3PLAYERINFO *mpi = (MP3PLAYERINFO *) radio.rc_pip->pi_bcast;
if (mpi) {
fprintf(stream, "<h2>MP3 Decoder</h2>\r\n");
fprintf(stream, "Layer = %d<br>\r\n", mpi->mpi_frameinfo.layer);
fprintf(stream, "Version = %d<br>\r\n", mpi->mpi_frameinfo.version);
fprintf(stream, "Bitrate = %d<br>\r\n", mpi->mpi_frameinfo.bitrate);
fprintf(stream, "Channels = %d<br>\r\n", mpi->mpi_frameinfo.nChans);
fprintf(stream, "Sample rate = %d<br>\r\n", mpi->mpi_frameinfo.samprate);
fprintf(stream, "Bits per sample = %d<br>\r\n", mpi->mpi_frameinfo.bitsPerSample);
fflush(stream);
}
}
}
}
fputs("<BR><BR><a href=\"/index.html\">BACK</a>", stream);
/* Send HTML footer and flush output buffer. */
fputs("</BODY></HTML>", stream);
fflush(stream);
return 0;
}
/*
* CGI Radio Control
*/
int CgiControl(FILE * stream, REQUEST * req)
{
int lvol = EMPTY_GAIN;
int rvol = EMPTY_GAIN;
int stop = 0;
NutHttpSendHeaderTop(stream, req, 200, "Ok");
NutHttpSendHeaderBot(stream, html_mt, -1);
/* Send HTML header. */
fputs("<HTML><BODY><BR><H1>Control Result</H1><BR><BR>", stream);
fflush(stream);
if (req->req_query) {
char *name;
char *value;
int i;
int count;
count = NutHttpGetParameterCount(req);
/* Extract count parameters. */
for (i = 0; i < count; i++) {
name = NutHttpGetParameterName(req, i);
value = NutHttpGetParameterValue(req, i);
/* Send the parameters back to the client. */
//fprintf(stream, "%s = '%s'<BR>\r\n", name, value);
if (strcmp(name, "lvol") == 0 && value[0]) {
lvol = atoi(value);
}
else if (strcmp(name, "rvol") == 0 && value[0]) {
rvol = atoi(value);
}
else if (strcmp(name, "stream") == 0 && value[0]) {
stop = 1;
}
}
/* Handle volume changes. User needs to fill one channel only
in order to set both. */
if (rvol == EMPTY_GAIN) {
rvol = lvol;
}
else if (lvol == EMPTY_GAIN) {
lvol = rvol;
}
if (lvol != EMPTY_GAIN) {
Tlv320DacSetVolume(lvol, rvol);
fprintf(stream, "Volume set to %d/%d dB<BR>.\r\n", lvol, rvol);
}
/* Handle station re-connect. */
if (stop) {
if (radio.rc_rip) {
ReceiverStop(radio.rc_rip);
fprintf(stream, "Stream stopped.<BR>\r\n");
}
if (radio.rc_pip) {
PlayerStop(radio.rc_pip);
fprintf(stream, "Player stopped.<BR>\r\n");
}
}
}
fputs("<BR><BR><p><a href=\"/index.html\">BACK</a></BODY></HTML></p>", stream);
fflush(stream);
return 0;
}
/*! \fn Service(void *arg)
* \brief HTTP service thread.
*
* The endless loop in this thread waits for a client connect,
* processes the HTTP request and disconnects. Nut/Net doesn't
* support a server backlog. If one client has established a
* connection, further connect attempts will be rejected.
* Typically browsers open more than one connection in order
* to load images concurrently. So we run this routine by
* several threads.
*
*/
THREAD(Service, arg)
{
TCPSOCKET *sock;
FILE *stream;
u_char id = (u_char) ((uptr_t) arg);
/*
* Now loop endless for connections.
*/
for (;;) {
/*
* Create a socket.
*/
if ((sock = NutTcpCreateSocket()) == 0) {
printf("[%u] Creating socket failed\n", id);
NutSleep(5000);
continue;
}
/*
* Listen on port 80. This call will block until we get a connection
* from a client.
*/
NutTcpAccept(sock, 80);
/*
* Wait until at least 8 kByte of free RAM is available. This will
* keep the client connected in low memory situations.
*/
while (NutHeapAvailable() < 8192) {
printf("[%u] Low mem\n", id);
NutSleep(1000);
}
/*
* Associate a stream with the socket so we can use standard I/O calls.
*/
if ((stream = _fdopen((int) ((uptr_t) sock), "r+b")) == 0) {
printf("[%u] Creating stream device failed\n", id);
} else {
/*
* This API call saves us a lot of work. It will parse the
* client's HTTP request, send any requested file from the
* registered file system or handle CGI requests by calling
* our registered CGI routine.
*/
NutHttpProcessRequest(stream);
/*
* Destroy the virtual stream device.
*/
fclose(stream);
}
/*
* Close our socket.
*/
NutTcpCloseSocket(sock);
}
}
/*!
* \brief HTTP Server Start.
*/
int HttpServerStart(void)
{
int i;
/*
* Register our device for the file system.
*/
NutRegisterDevice(&MY_FSDEV, 0, 0);
#ifdef MY_HTTPROOT
/* Register root path. */
printf("Registering HTTP root '" MY_HTTPROOT "'...");
if (NutRegisterHttpRoot(MY_HTTPROOT)) {
puts("failed");
for (;;);
}
puts("OK");
#endif
/*
* Register our CGIs.
*/
NutRegisterCgi("info.cgi", CgiInfo);
NutRegisterCgi("control.cgi", CgiControl);
/*
* Protect the admin directory with user and password.
*/
NutRegisterAuth("admin", "admin:lemta");
/*
* Start four server threads.
*/
for (i = 1; i <= 4; i++) {
char *thname = "httpd0";
thname[5] = '0' + i;
NutThreadCreate(thname, Service, (void *) (uptr_t) i, HTTPD_THREAD_STACK);
}
return 0;
}
#endif /* USE_HTTPSERVER */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -