mirror of
https://github.com/JamesonHuang/OpenWrt_Luci_Lua.git
synced 2024-11-23 22:00:11 +00:00
ajd dir name
This commit is contained in:
parent
70f503595e
commit
ceea7d2352
Binary file not shown.
Before Width: | Height: | Size: 446 KiB |
@ -1,249 +0,0 @@
|
||||
/************************************************
|
||||
*
|
||||
* file : DownloadTask.h
|
||||
* author: bobding
|
||||
* date : 2014-09-25
|
||||
* detail:
|
||||
*
|
||||
************************************************/
|
||||
|
||||
#ifndef _DOWNLOADTASK_H_
|
||||
#define _DOWNLOADTASK_H_
|
||||
|
||||
#include "TcpSocket.h"
|
||||
#include "HttpUtils.h"
|
||||
|
||||
#define MAXBUFFERSIZE 2048
|
||||
|
||||
//TcpSocket* socket;
|
||||
/*
|
||||
void initDownloadTask()
|
||||
{
|
||||
//socket = 0;
|
||||
initTcpSocket();
|
||||
//socket = new TcpSocket();
|
||||
}
|
||||
|
||||
void destroyDownloadTask()
|
||||
{
|
||||
if (0 != socket)
|
||||
{
|
||||
free(socket);
|
||||
}
|
||||
|
||||
socket = 0;
|
||||
}
|
||||
*/
|
||||
|
||||
int taskStart(const char* url, char** buffer, unsigned int* length)
|
||||
{
|
||||
/*if (0 == socket)
|
||||
{
|
||||
LogError("[DownloadTask::Start] invalid socket, url: %s.\n", url);
|
||||
return -1;
|
||||
}*/
|
||||
|
||||
int ret = socketOpen();
|
||||
if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct HttpUrl httpUrl = ParseUrl(url);
|
||||
|
||||
ret = socketConnect(httpUrl.host, httpUrl.port);
|
||||
if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = socketSend(httpUrl.request, strlen(httpUrl.request));
|
||||
if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
char dummy[MAXBUFFERSIZE];
|
||||
ret = socketRecv(dummy, MAXBUFFERSIZE); // receive 1st package.
|
||||
if (ret <= 0)
|
||||
{
|
||||
LogError("[DownloadTask::Start] recv head 0 bytes, url: %s.\n", url);
|
||||
return -1;
|
||||
}
|
||||
|
||||
dummy[ret] = '\0';
|
||||
|
||||
struct HttpHead httpHead = ParseHead(dummy, strlen(dummy));
|
||||
if (HS_FAIL == httpHead.httpState)
|
||||
{
|
||||
LogError("[DownloadTask::Start] recv failed, url: %s.\n", url);
|
||||
return -1;
|
||||
}
|
||||
else if (HS_RETRY == httpHead.httpState)
|
||||
{
|
||||
LogWarn("[DownloadTask::Start] retry, url: %s.\n", url);
|
||||
return taskStart(url, buffer, length);
|
||||
}
|
||||
else if (HS_REDIR == httpHead.httpState)
|
||||
{
|
||||
LogWarn("[DownloadTask::Start] redir, url: %s, location: %s.\n", url, httpHead.location);
|
||||
return taskStart(httpHead.location, buffer, length);
|
||||
}
|
||||
|
||||
*length = httpHead.contentLength;
|
||||
*buffer = (char *)malloc(*length * sizeof(char*));
|
||||
//*buffer = new char[*length];
|
||||
memset(*buffer, 0, *length);
|
||||
unsigned int offset = 0;
|
||||
|
||||
if (ret > httpHead.headLength)
|
||||
{
|
||||
memcpy((*buffer) + offset, dummy + httpHead.headLength, ret - httpHead.headLength);
|
||||
offset += ret - httpHead.headLength;
|
||||
}
|
||||
|
||||
while ((ret = socketRecv(dummy, MAXBUFFERSIZE)) >= 0)
|
||||
{
|
||||
LogPrompt("[DownloadTask::Start] received: %d, %d / %d bytes.\n", ret, offset, *length);
|
||||
|
||||
if (offset >= *length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (0 == ret)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (offset + ret > *length)
|
||||
{
|
||||
ret = *length - offset;
|
||||
}
|
||||
|
||||
memcpy((*buffer) + offset, dummy, ret);
|
||||
|
||||
offset += ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
class DownloadTask
|
||||
{
|
||||
public:
|
||||
DownloadTask()
|
||||
{
|
||||
socket = 0;
|
||||
socket = new TcpSocket();
|
||||
}
|
||||
|
||||
~DownloadTask()
|
||||
{
|
||||
if (0 != socket)
|
||||
{
|
||||
delete socket;
|
||||
}
|
||||
|
||||
socket = 0;
|
||||
}
|
||||
|
||||
int Start(const char* url, char** buffer, unsigned int* length)
|
||||
{
|
||||
if (0 == socket)
|
||||
{
|
||||
LogError("[DownloadTask::Start] invalid socket, url: %s.\n", url);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret = socket->Open();
|
||||
if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
HttpUrl httpUrl = HttpUtils::ParseUrl(url);
|
||||
|
||||
ret = socket->Connect(httpUrl.host, httpUrl.port);
|
||||
if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = socket->Send(httpUrl.request, strlen(httpUrl.request));
|
||||
if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
char dummy[MAXBUFFERSIZE];
|
||||
ret = socket->Recv(dummy, MAXBUFFERSIZE); // receive 1st package.
|
||||
if (ret <= 0)
|
||||
{
|
||||
LogError("[DownloadTask::Start] recv head 0 bytes, url: %s.\n", url);
|
||||
return -1;
|
||||
}
|
||||
|
||||
dummy[ret] = '\0';
|
||||
|
||||
HttpHead httpHead = HttpUtils::ParseHead(dummy, strlen(dummy));
|
||||
if (HS_FAIL == httpHead.httpState)
|
||||
{
|
||||
LogError("[DownloadTask::Start] recv failed, url: %s.\n", url);
|
||||
return -1;
|
||||
}
|
||||
else if (HS_RETRY == httpHead.httpState)
|
||||
{
|
||||
LogWarn("[DownloadTask::Start] retry, url: %s.\n", url);
|
||||
return Start(url, buffer, length);
|
||||
}
|
||||
else if (HS_REDIR == httpHead.httpState)
|
||||
{
|
||||
LogWarn("[DownloadTask::Start] redir, url: %s, location: %s.\n", url, httpHead.location);
|
||||
return Start(httpHead.location, buffer, length);
|
||||
}
|
||||
|
||||
*length = httpHead.contentLength;
|
||||
*buffer = new char[*length];
|
||||
memset(*buffer, 0, *length);
|
||||
unsigned int offset = 0;
|
||||
|
||||
if (ret > httpHead.headLength)
|
||||
{
|
||||
memcpy((*buffer) + offset, dummy + httpHead.headLength, ret - httpHead.headLength);
|
||||
offset += ret - httpHead.headLength;
|
||||
}
|
||||
|
||||
while ((ret = socket->Recv(dummy, MAXBUFFERSIZE)) >= 0)
|
||||
{
|
||||
LogPrompt("[DownloadTask::Start] received: %d, %d / %d bytes.\n", ret, offset, *length);
|
||||
|
||||
if (offset >= *length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (0 == ret)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (offset + ret > *length)
|
||||
{
|
||||
ret = *length - offset;
|
||||
}
|
||||
|
||||
memcpy((*buffer) + offset, dummy, ret);
|
||||
|
||||
offset += ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
TcpSocket* socket;
|
||||
};
|
||||
*/
|
||||
#endif // _DOWNLOADTASK_H_
|
@ -1,249 +0,0 @@
|
||||
/************************************************
|
||||
*
|
||||
* file : HttpUtils.h
|
||||
* author: bobding
|
||||
* date : 2014-09-25
|
||||
* detail:
|
||||
*
|
||||
************************************************/
|
||||
|
||||
#ifndef _HTTPUTILS_H_
|
||||
#define _HTTPUTILS_H_
|
||||
|
||||
#define MAXURLLENGTH 1024
|
||||
|
||||
struct HttpUrl
|
||||
{
|
||||
char host[MAXURLLENGTH]; // host
|
||||
char file[MAXURLLENGTH]; // file
|
||||
unsigned short port; // port
|
||||
char request[MAXURLLENGTH]; // request
|
||||
};
|
||||
|
||||
enum HttpState
|
||||
{
|
||||
HS_FAIL,
|
||||
HS_SUCC,
|
||||
HS_RETRY,
|
||||
HS_REDIR,
|
||||
};
|
||||
|
||||
struct HttpHead
|
||||
{
|
||||
unsigned int httpCode; // http code
|
||||
enum HttpState httpState; // state
|
||||
unsigned int contentLength; // content length
|
||||
unsigned int headLength; // head length
|
||||
char location[MAXURLLENGTH];// redirection
|
||||
};
|
||||
|
||||
static struct HttpUrl ParseUrl(const char* url)
|
||||
{
|
||||
struct HttpUrl httpUrl;
|
||||
memset(&httpUrl, 0, sizeof(struct HttpUrl));
|
||||
|
||||
unsigned int length = strlen(url);
|
||||
if (length > MAXURLLENGTH)
|
||||
{
|
||||
LogError("[HttpUtils::ParseUrl] url is too long, url: %s.\n", url);
|
||||
return httpUrl;
|
||||
}
|
||||
|
||||
memcpy(httpUrl.host, url, strlen(url));
|
||||
|
||||
const char* s = strchr(httpUrl.host, '/');
|
||||
if (0 != s)
|
||||
{
|
||||
httpUrl.host[s - httpUrl.host] = '\0';
|
||||
s++;
|
||||
memcpy(httpUrl.file, s, strlen(s));
|
||||
}
|
||||
|
||||
s = strchr(httpUrl.host, ':');
|
||||
if (0 != s)
|
||||
{
|
||||
httpUrl.host[s - httpUrl.host] = '\0';
|
||||
s++;
|
||||
httpUrl.port = (unsigned short)atoi(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
httpUrl.port = 80;
|
||||
}
|
||||
|
||||
snprintf(httpUrl.request, MAXURLLENGTH - 1,
|
||||
"GET /%s HTTP/1.1\r\n"
|
||||
"Host: %s\r\n\r\n",
|
||||
httpUrl.file, httpUrl.host);
|
||||
|
||||
LogPrompt("[HttpUtils::ParseUrl] host: %s, file: %s, port: %d.\n", httpUrl.host, httpUrl.file, httpUrl.port);
|
||||
return httpUrl;
|
||||
}
|
||||
|
||||
static struct HttpHead ParseHead(const char* buffer, unsigned int length)
|
||||
{
|
||||
struct HttpHead httpHead;
|
||||
memset(&httpHead, 0, sizeof(struct HttpHead));
|
||||
|
||||
const char* s = strstr(buffer, "HTTP/1.1 ");
|
||||
if (0 != s)
|
||||
{
|
||||
httpHead.httpCode = (unsigned int)atoi(s + strlen("HTTP/1.1 "));
|
||||
}
|
||||
|
||||
if (200 == httpHead.httpCode || 206 == httpHead.httpCode)
|
||||
{
|
||||
httpHead.httpState = HS_SUCC;
|
||||
}
|
||||
else if (201 == httpHead.httpCode)
|
||||
{
|
||||
httpHead.httpState = HS_REDIR;
|
||||
}
|
||||
else if (202 == httpHead.httpCode)
|
||||
{
|
||||
httpHead.httpState = HS_RETRY;
|
||||
}
|
||||
else if (httpHead.httpCode >= 300 && httpHead.httpCode < 400)
|
||||
{
|
||||
httpHead.httpState = HS_REDIR;
|
||||
}
|
||||
else
|
||||
{
|
||||
httpHead.httpState = HS_FAIL;
|
||||
}
|
||||
|
||||
if (HS_REDIR == httpHead.httpState && 0 != (s = strstr(buffer, "Location: ")))
|
||||
{
|
||||
const char* e = strstr(s, "\r\n");
|
||||
s += strlen("Location: ");
|
||||
memcpy(httpHead.location, s, (0 == e) ? strlen(s) : (e - s));
|
||||
}
|
||||
|
||||
s = strstr(buffer, "Content-Length: ");
|
||||
if (0 != s)
|
||||
{
|
||||
httpHead.contentLength = (unsigned int)atoi(s + strlen("Content-Length: "));
|
||||
}
|
||||
|
||||
s = strstr(buffer, "\r\n\r\n");
|
||||
if (0 != s)
|
||||
{
|
||||
httpHead.headLength = s + strlen("\r\n\r\n") - buffer;
|
||||
}
|
||||
|
||||
LogPrompt("[HttpUtils::ParseHead] httpCode: %d, contentLength: %d, headLength: %d.\n",
|
||||
httpHead.httpCode, httpHead.contentLength, httpHead.headLength);
|
||||
|
||||
return httpHead;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
class HttpUtils
|
||||
{
|
||||
public:
|
||||
static HttpUrl ParseUrl(const char* url)
|
||||
{
|
||||
HttpUrl httpUrl;
|
||||
memset(&httpUrl, 0, sizeof(HttpUrl));
|
||||
|
||||
unsigned int length = strlen(url);
|
||||
if (length > MAXURLLENGTH)
|
||||
{
|
||||
LogError("[HttpUtils::ParseUrl] url is too long, url: %s.\n", url);
|
||||
return httpUrl;
|
||||
}
|
||||
|
||||
memcpy(httpUrl.host, url, strlen(url));
|
||||
|
||||
const char* s = strchr(httpUrl.host, '/');
|
||||
if (0 != s)
|
||||
{
|
||||
httpUrl.host[s - httpUrl.host] = '\0';
|
||||
s++;
|
||||
memcpy(httpUrl.file, s, strlen(s));
|
||||
}
|
||||
|
||||
s = strchr(httpUrl.host, ':');
|
||||
if (0 != s)
|
||||
{
|
||||
httpUrl.host[s - httpUrl.host] = '\0';
|
||||
s++;
|
||||
httpUrl.port = (unsigned short)atoi(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
httpUrl.port = 80;
|
||||
}
|
||||
|
||||
snprintf(httpUrl.request, MAXURLLENGTH - 1,
|
||||
"GET /%s HTTP/1.1\r\n"
|
||||
"Host: %s\r\n\r\n",
|
||||
httpUrl.file, httpUrl.host);
|
||||
|
||||
LogPrompt("[HttpUtils::ParseUrl] host: %s, file: %s, port: %d.\n", httpUrl.host, httpUrl.file, httpUrl.port);
|
||||
|
||||
return httpUrl;
|
||||
}
|
||||
|
||||
static HttpHead ParseHead(const char* buffer, unsigned int length)
|
||||
{
|
||||
HttpHead httpHead;
|
||||
memset(&httpHead, 0, sizeof(HttpHead));
|
||||
|
||||
const char* s = strstr(buffer, "HTTP/1.1 ");
|
||||
if (0 != s)
|
||||
{
|
||||
httpHead.httpCode = (unsigned int)atoi(s + strlen("HTTP/1.1 "));
|
||||
}
|
||||
|
||||
if (200 == httpHead.httpCode || 206 == httpHead.httpCode)
|
||||
{
|
||||
httpHead.httpState = HS_SUCC;
|
||||
}
|
||||
else if (201 == httpHead.httpCode)
|
||||
{
|
||||
httpHead.httpState = HS_REDIR;
|
||||
}
|
||||
else if (202 == httpHead.httpCode)
|
||||
{
|
||||
httpHead.httpState = HS_RETRY;
|
||||
}
|
||||
else if (httpHead.httpCode >= 300 && httpHead.httpCode < 400)
|
||||
{
|
||||
httpHead.httpState = HS_REDIR;
|
||||
}
|
||||
else
|
||||
{
|
||||
httpHead.httpState = HS_FAIL;
|
||||
}
|
||||
|
||||
if (HS_REDIR == httpHead.httpState && 0 != (s = strstr(buffer, "Location: ")))
|
||||
{
|
||||
const char* e = strstr(s, "\r\n");
|
||||
s += strlen("Location: ");
|
||||
memcpy(httpHead.location, s, (0 == e) ? strlen(s) : (e - s));
|
||||
}
|
||||
|
||||
s = strstr(buffer, "Content-Length: ");
|
||||
if (0 != s)
|
||||
{
|
||||
httpHead.contentLength = (unsigned int)atoi(s + strlen("Content-Length: "));
|
||||
}
|
||||
|
||||
s = strstr(buffer, "\r\n\r\n");
|
||||
if (0 != s)
|
||||
{
|
||||
httpHead.headLength = s + strlen("\r\n\r\n") - buffer;
|
||||
}
|
||||
|
||||
LogPrompt("[HttpUtils::ParseHead] httpCode: %d, contentLength: %d, headLength: %d.\n",
|
||||
httpHead.httpCode, httpHead.contentLength, httpHead.headLength);
|
||||
|
||||
return httpHead;
|
||||
}
|
||||
};
|
||||
*/
|
||||
|
||||
#endif // _HTTPUTILS_H_
|
@ -1,322 +0,0 @@
|
||||
/************************************************
|
||||
*
|
||||
* file : Log.h
|
||||
* author: bobding
|
||||
* date : 2014-09-24
|
||||
* detail:
|
||||
*
|
||||
************************************************/
|
||||
|
||||
#ifndef _LOG_H_
|
||||
#define _LOG_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
#include <memory.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#define LogCritical Critical
|
||||
#define LogError Error
|
||||
#define LogWarn Warn
|
||||
#define LogPrompt Prompt
|
||||
|
||||
#define LOGPATH "./1.log"
|
||||
|
||||
FILE* handle;
|
||||
/*
|
||||
static Log* Instance()
|
||||
{
|
||||
static Log instance;
|
||||
return &instance;
|
||||
}
|
||||
*/
|
||||
|
||||
const char* FormatedTime()
|
||||
{
|
||||
static char strDate[128];
|
||||
memset(strDate, 0, sizeof(strDate));
|
||||
|
||||
struct timeval tv;
|
||||
int ret = gettimeofday(&tv, 0);
|
||||
if (ret < 0)
|
||||
{
|
||||
return strDate;
|
||||
}
|
||||
|
||||
struct tm* pt = localtime(&tv.tv_sec);
|
||||
snprintf(strDate, 127, "%d-%02d-%02d %02d:%02d:%02d.%03d", pt->tm_year + 1900, pt->tm_mon + 1, pt->tm_mday, pt->tm_hour, pt->tm_min, pt->tm_sec, (int)(tv.tv_usec / 1000));
|
||||
|
||||
return strDate;
|
||||
}
|
||||
|
||||
void initLog()
|
||||
{
|
||||
handle = 0;
|
||||
if (-1 != access(LOGPATH, F_OK))
|
||||
{
|
||||
remove(LOGPATH);
|
||||
}
|
||||
handle = fopen(LOGPATH, "w");
|
||||
}
|
||||
|
||||
void destroyLog()
|
||||
{
|
||||
if (0 != handle)
|
||||
{
|
||||
fclose(handle);
|
||||
}
|
||||
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
// flush every item
|
||||
int Critical(const char* fmt, ...)
|
||||
{
|
||||
initLog();
|
||||
static char buffer[128 + 1024];
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
snprintf(buffer, 128, "%s [%-8s] ", FormatedTime(), "Critical");
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int length = vsnprintf((char*)(buffer+strlen(buffer)), 1023, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
fwrite(buffer, strlen(buffer), 1, handle);
|
||||
fflush(handle);
|
||||
|
||||
printf("%s", buffer);
|
||||
destroyLog();
|
||||
return length;
|
||||
}
|
||||
|
||||
// flush every item
|
||||
int Error(const char* fmt, ...)
|
||||
{
|
||||
initLog();
|
||||
if (0 == handle)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static char buffer[128 + 1024];
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
snprintf(buffer, 128, "%s [%-8s] ", FormatedTime(), "Error");
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int length = vsnprintf((char*)(buffer+strlen(buffer)), 1023, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
fwrite(buffer, strlen(buffer), 1, handle);
|
||||
fflush(handle);
|
||||
|
||||
printf("%s", buffer);
|
||||
destroyLog();
|
||||
return length;
|
||||
}
|
||||
|
||||
// flush every 100 items, careless lost
|
||||
int Warn(const char* fmt, ...)
|
||||
{
|
||||
initLog();
|
||||
static unsigned int maxCnt = 100;
|
||||
static unsigned int curCnt = 0;
|
||||
|
||||
if (0 == handle)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static char buffer[128 + 1024];
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
snprintf(buffer, 128, "%s [%-8s] ", FormatedTime(), "Warn");
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int length = vsnprintf((char*)(buffer+strlen(buffer)), 1023, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
fwrite(buffer, strlen(buffer), 1, handle);
|
||||
|
||||
if (++curCnt >= maxCnt)
|
||||
{
|
||||
fflush(handle);
|
||||
curCnt = 0;
|
||||
}
|
||||
|
||||
printf("%s", buffer);
|
||||
destroyLog();
|
||||
return length;
|
||||
}
|
||||
|
||||
// stdout
|
||||
int Prompt(const char* fmt, ...)
|
||||
{
|
||||
initLog();
|
||||
static char buffer[128 + 1024];
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
snprintf(buffer, 128, "%s [%-8s] ", FormatedTime(), "Prompt");
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int length = vsnprintf((char*)(buffer+strlen(buffer)), 1023, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
printf("%s", buffer);
|
||||
destroyLog();
|
||||
return length;
|
||||
}
|
||||
|
||||
/*
|
||||
class Log
|
||||
{
|
||||
private:
|
||||
FILE* handle;
|
||||
|
||||
public:
|
||||
static Log* Instance()
|
||||
{
|
||||
static Log instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
public:
|
||||
Log() : handle(0)
|
||||
{
|
||||
if (-1 != access(LOGPATH, F_OK))
|
||||
{
|
||||
remove(LOGPATH);
|
||||
}
|
||||
|
||||
handle = fopen(LOGPATH, "w");
|
||||
}
|
||||
|
||||
~Log()
|
||||
{
|
||||
if (0 != handle)
|
||||
{
|
||||
fclose(handle);
|
||||
}
|
||||
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
// flush every item
|
||||
int Critical(const char* fmt, ...)
|
||||
{
|
||||
static char buffer[128 + 1024];
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
snprintf(buffer, 128, "%s [%-8s] ", FormatedTime(), "Critical");
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int length = vsnprintf((char*)(buffer+strlen(buffer)), 1023, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
fwrite(buffer, strlen(buffer), 1, handle);
|
||||
fflush(handle);
|
||||
|
||||
printf("%s", buffer);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
// flush every item
|
||||
int Error(const char* fmt, ...)
|
||||
{
|
||||
if (0 == handle)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static char buffer[128 + 1024];
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
snprintf(buffer, 128, "%s [%-8s] ", FormatedTime(), "Error");
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int length = vsnprintf((char*)(buffer+strlen(buffer)), 1023, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
fwrite(buffer, strlen(buffer), 1, handle);
|
||||
fflush(handle);
|
||||
|
||||
printf("%s", buffer);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
// flush every 100 items, careless lost
|
||||
int Warn(const char* fmt, ...)
|
||||
{
|
||||
static unsigned int maxCnt = 100;
|
||||
static unsigned int curCnt = 0;
|
||||
|
||||
if (0 == handle)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static char buffer[128 + 1024];
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
snprintf(buffer, 128, "%s [%-8s] ", FormatedTime(), "Warn");
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int length = vsnprintf((char*)(buffer+strlen(buffer)), 1023, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
fwrite(buffer, strlen(buffer), 1, handle);
|
||||
|
||||
if (++curCnt >= maxCnt)
|
||||
{
|
||||
fflush(handle);
|
||||
curCnt = 0;
|
||||
}
|
||||
|
||||
printf("%s", buffer);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
// stdout
|
||||
int Prompt(const char* fmt, ...)
|
||||
{
|
||||
static char buffer[128 + 1024];
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
snprintf(buffer, 128, "%s [%-8s] ", FormatedTime(), "Prompt");
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int length = vsnprintf((char*)(buffer+strlen(buffer)), 1023, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
printf("%s", buffer);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
protected:
|
||||
const char* FormatedTime()
|
||||
{
|
||||
static char strDate[128];
|
||||
memset(strDate, 0, sizeof(strDate));
|
||||
|
||||
struct timeval tv;
|
||||
int ret = gettimeofday(&tv, 0);
|
||||
if (ret < 0)
|
||||
{
|
||||
return strDate;
|
||||
}
|
||||
|
||||
struct tm* pt = localtime(&tv.tv_sec);
|
||||
snprintf(strDate, 127, "%d-%02d-%02d %02d:%02d:%02d.%03d", pt->tm_year + 1900, pt->tm_mon + 1, pt->tm_mday, pt->tm_hour, pt->tm_min, pt->tm_sec, (int)(tv.tv_usec / 1000));
|
||||
|
||||
return strDate;
|
||||
}
|
||||
};
|
||||
*/
|
||||
#endif // _LOG_H_
|
@ -1,11 +0,0 @@
|
||||
http-download
|
||||
=============
|
||||
|
||||
download demo use http get implement by socket programmed in cpp.
|
||||
|
||||
dev env: ubuntu 64
|
||||
run env: ubuntu 64
|
||||
|
||||
todo list:
|
||||
1. multi-threading supported
|
||||
2. continue download
|
@ -1,246 +0,0 @@
|
||||
/************************************************
|
||||
*
|
||||
* file : TcpSocket.h
|
||||
* author: bobding
|
||||
* date : 2014-09-25
|
||||
* detail:
|
||||
*
|
||||
************************************************/
|
||||
|
||||
#ifndef _TCPSOCKET_H_
|
||||
#define _TCPSOCKET_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
#include "Log.h"
|
||||
|
||||
int sockfd;
|
||||
|
||||
void initTcpSocket(){
|
||||
sockfd = -1;
|
||||
}
|
||||
|
||||
// return >=0 means success, otherwise failed.
|
||||
int socketOpen(){
|
||||
sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (sockfd < 0)
|
||||
{
|
||||
LogError("[Socket::Open] create socket failed: %s.\n", strerror(errno));
|
||||
}
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
// host: 127.0.0.1 or www.qq.com
|
||||
// return 0 means success, otherwise failed.
|
||||
int socketConnect(const char* host, unsigned short port)
|
||||
{
|
||||
if (sockfd < 0)
|
||||
{
|
||||
LogError("[Socket::Connect] invalid sockfd.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
|
||||
struct hostent* h = gethostbyname(host);
|
||||
if (0 == h)
|
||||
{
|
||||
LogError("[Socket::Connect] gethostbyname failed: %s.\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr.sin_addr = *(struct in_addr*)h->h_addr_list[0];
|
||||
|
||||
int ret = connect(sockfd, (const struct sockaddr*)&addr, sizeof(addr));
|
||||
if (ret < 0)
|
||||
{
|
||||
LogError("[Socket::Connect] connect failed: %s.\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// return send bytes, -1 means failed.
|
||||
int socketSend(const char* buffer, unsigned int length)
|
||||
{
|
||||
if (sockfd < 0)
|
||||
{
|
||||
LogError("[Socket::Send] invalid sockfd.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret = send(sockfd, buffer, length, 0);
|
||||
if (ret < 0)
|
||||
{
|
||||
LogError("[Socket::Send] send failed: %s.\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// return recv bytes, -1 means failed.
|
||||
int socketRecv(char* buffer, unsigned int length)
|
||||
{
|
||||
if (sockfd < 0)
|
||||
{
|
||||
LogError("[Socket::Recv] invalid sockfd.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret = recv(sockfd, buffer, length, 0);
|
||||
if (ret < 0)
|
||||
{
|
||||
LogError("[Socket::Recv] recv failed: %s.\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// return 0 success, otherwise failed.
|
||||
int Close()
|
||||
{
|
||||
int ret = close(sockfd);
|
||||
if (ret < 0)
|
||||
{
|
||||
LogError("[Socket::Close] close socket failed: %s.\n", strerror(errno));
|
||||
}
|
||||
|
||||
sockfd = -1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int GetSocket()
|
||||
{
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
/*
|
||||
class TcpSocket
|
||||
{
|
||||
public:
|
||||
TcpSocket()
|
||||
{
|
||||
sockfd = -1;
|
||||
}
|
||||
|
||||
// return >=0 means success, otherwise failed.
|
||||
int Open()
|
||||
{
|
||||
sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (sockfd < 0)
|
||||
{
|
||||
LogError("[Socket::Open] create socket failed: %s.\n", strerror(errno));
|
||||
}
|
||||
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
// host: 127.0.0.1 or www.qq.com
|
||||
// return 0 means success, otherwise failed.
|
||||
int Connect(const char* host, unsigned short port)
|
||||
{
|
||||
if (sockfd < 0)
|
||||
{
|
||||
LogError("[Socket::Connect] invalid sockfd.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
|
||||
struct hostent* h = gethostbyname(host);
|
||||
if (0 == h)
|
||||
{
|
||||
LogError("[Socket::Connect] gethostbyname failed: %s.\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr.sin_addr = *(struct in_addr*)h->h_addr_list[0];
|
||||
|
||||
int ret = connect(sockfd, (const struct sockaddr*)&addr, sizeof(addr));
|
||||
if (ret < 0)
|
||||
{
|
||||
LogError("[Socket::Connect] connect failed: %s.\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// return send bytes, -1 means failed.
|
||||
int Send(const char* buffer, unsigned int length)
|
||||
{
|
||||
if (sockfd < 0)
|
||||
{
|
||||
LogError("[Socket::Send] invalid sockfd.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret = send(sockfd, buffer, length, 0);
|
||||
if (ret < 0)
|
||||
{
|
||||
LogError("[Socket::Send] send failed: %s.\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// return recv bytes, -1 means failed.
|
||||
int Recv(char* buffer, unsigned int length)
|
||||
{
|
||||
if (sockfd < 0)
|
||||
{
|
||||
LogError("[Socket::Recv] invalid sockfd.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret = recv(sockfd, buffer, length, 0);
|
||||
if (ret < 0)
|
||||
{
|
||||
LogError("[Socket::Recv] recv failed: %s.\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// return 0 success, otherwise failed.
|
||||
int Close()
|
||||
{
|
||||
int ret = close(sockfd);
|
||||
if (ret < 0)
|
||||
{
|
||||
LogError("[Socket::Close] close socket failed: %s.\n", strerror(errno));
|
||||
}
|
||||
|
||||
sockfd = -1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int GetSocket()
|
||||
{
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
private:
|
||||
int sockfd;
|
||||
};
|
||||
*/
|
||||
|
||||
#endif // _TCPSOCKET_H_
|
Binary file not shown.
@ -1,66 +0,0 @@
|
||||
/************************************************
|
||||
*
|
||||
* file : main.cpp
|
||||
* author: bobding
|
||||
* date : 2014-09-25
|
||||
* detail:
|
||||
*
|
||||
************************************************/
|
||||
|
||||
#include "TcpSocket.h"
|
||||
#include "DownloadTask.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
/*
|
||||
TcpSocket* socket = new TcpSocket();
|
||||
|
||||
int ret = socket->Open();
|
||||
|
||||
ret = socket->Connect("image.modoomarble.qq.com", 80);
|
||||
//ret = socket->Connect("14.17.32.211");
|
||||
|
||||
const char* s = "GET /modoomarble/data610_spr_1.zip HTTP/1.1\r\n"
|
||||
"Host: image.modoomarble.qq.com\r\n\r\n";
|
||||
|
||||
ret = socket->Send(s, strlen(s));
|
||||
|
||||
char buffer[512];
|
||||
ret = socket->Recv(buffer, 512);
|
||||
buffer[ret] = 0;
|
||||
printf("length: %d\n %s\n", ret, buffer);
|
||||
|
||||
ret = socket->Close();
|
||||
|
||||
ret = socket->Open();
|
||||
|
||||
ret = socket->Connect("www.qq.com", 80);
|
||||
|
||||
s = "GET / HTTP/1.1\r\n"
|
||||
"Host: www.qq.com\r\n\r\n";
|
||||
|
||||
ret = socket->Send(s, strlen(s));
|
||||
|
||||
ret = socket->Recv(buffer, 512);
|
||||
buffer[ret] = 0;
|
||||
printf("length: %d\n %s\n", ret, buffer);
|
||||
*/
|
||||
|
||||
char* buffer = 0;
|
||||
unsigned int length = 0;
|
||||
|
||||
//DownloadTask task;
|
||||
//int ret = task.Start("image.modoomarble.qq.com/modoomarble/data610_spr_1.zip", &buffer, &length);
|
||||
int ret = taskStart("images.china.cn/attachement/jpg/site1000/20150611/002564bb43f116e36f8c4c.jpg", &buffer, &length);
|
||||
|
||||
if (ret >= 0)
|
||||
{
|
||||
FILE* fp = fopen("002564bb43f116e36f8c4c.jpg", "w");
|
||||
fwrite(buffer, length, 1, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
|
||||
return 0;
|
||||
}
|
@ -1 +0,0 @@
|
||||
gcc -o main main.c
|
@ -1,61 +0,0 @@
|
||||
!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/
|
||||
!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/
|
||||
!_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/
|
||||
!_TAG_PROGRAM_NAME Exuberant Ctags //
|
||||
!_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/
|
||||
!_TAG_PROGRAM_VERSION 5.9~svn20110310 //
|
||||
Close TcpSocket.h /^ int Close()$/;" f class:TcpSocket
|
||||
Connect TcpSocket.h /^ int Connect(const char* host, unsigned short port)$/;" f class:TcpSocket
|
||||
Critical Log.h /^ int Critical(const char* fmt, ...)$/;" f class:Log
|
||||
DownloadTask DownloadTask.h /^ DownloadTask() : socket(0)$/;" f class:DownloadTask
|
||||
DownloadTask DownloadTask.h /^class DownloadTask$/;" c
|
||||
Error Log.h /^ int Error(const char* fmt, ...)$/;" f class:Log
|
||||
FormatedTime Log.h /^ const char* FormatedTime()$/;" f class:Log
|
||||
GetSocket TcpSocket.h /^ int GetSocket()$/;" f class:TcpSocket
|
||||
HS_FAIL HttpUtils.h /^ HS_FAIL,$/;" e enum:HttpState
|
||||
HS_REDIR HttpUtils.h /^ HS_REDIR,$/;" e enum:HttpState
|
||||
HS_RETRY HttpUtils.h /^ HS_RETRY,$/;" e enum:HttpState
|
||||
HS_SUCC HttpUtils.h /^ HS_SUCC,$/;" e enum:HttpState
|
||||
HttpHead HttpUtils.h /^struct HttpHead$/;" s
|
||||
HttpState HttpUtils.h /^enum HttpState$/;" g
|
||||
HttpUrl HttpUtils.h /^struct HttpUrl$/;" s
|
||||
HttpUtils HttpUtils.h /^class HttpUtils$/;" c
|
||||
Instance Log.h /^ static Log* Instance()$/;" f class:Log
|
||||
LOGPATH Log.h 25;" d
|
||||
Log Log.h /^ Log() : handle(0)$/;" f class:Log
|
||||
Log Log.h /^class Log$/;" c
|
||||
LogCritical Log.h 20;" d
|
||||
LogError Log.h 21;" d
|
||||
LogPrompt Log.h 23;" d
|
||||
LogWarn Log.h 22;" d
|
||||
MAXBUFFERSIZE DownloadTask.h 16;" d
|
||||
MAXURLLENGTH HttpUtils.h 13;" d
|
||||
Open TcpSocket.h /^ int Open()$/;" f class:TcpSocket
|
||||
ParseHead HttpUtils.h /^ static HttpHead ParseHead(const char* buffer, unsigned int length)$/;" f class:HttpUtils
|
||||
ParseUrl HttpUtils.h /^ static HttpUrl ParseUrl(const char* url)$/;" f class:HttpUtils
|
||||
Prompt Log.h /^ int Prompt(const char* fmt, ...)$/;" f class:Log
|
||||
Recv TcpSocket.h /^ int Recv(char* buffer, unsigned int length)$/;" f class:TcpSocket
|
||||
Send TcpSocket.h /^ int Send(const char* buffer, unsigned int length)$/;" f class:TcpSocket
|
||||
Start DownloadTask.h /^ int Start(const char* url, char** buffer, unsigned int* length)$/;" f class:DownloadTask
|
||||
TcpSocket TcpSocket.h /^ TcpSocket() : sockfd(-1)$/;" f class:TcpSocket
|
||||
TcpSocket TcpSocket.h /^class TcpSocket$/;" c
|
||||
Warn Log.h /^ int Warn(const char* fmt, ...)$/;" f class:Log
|
||||
_DOWNLOADTASK_H_ DownloadTask.h 11;" d
|
||||
_HTTPUTILS_H_ HttpUtils.h 11;" d
|
||||
_LOG_H_ Log.h 11;" d
|
||||
_TCPSOCKET_H_ TcpSocket.h 11;" d
|
||||
contentLength HttpUtils.h /^ unsigned int contentLength; \/\/ content length$/;" m struct:HttpHead
|
||||
file HttpUtils.h /^ char file[MAXURLLENGTH]; \/\/ file$/;" m struct:HttpUrl
|
||||
handle Log.h /^ FILE* handle;$/;" m class:Log
|
||||
headLength HttpUtils.h /^ unsigned int headLength; \/\/ head length$/;" m struct:HttpHead
|
||||
host HttpUtils.h /^ char host[MAXURLLENGTH]; \/\/ host$/;" m struct:HttpUrl
|
||||
httpCode HttpUtils.h /^ unsigned int httpCode; \/\/ http code$/;" m struct:HttpHead
|
||||
httpState HttpUtils.h /^ HttpState httpState; \/\/ state$/;" m struct:HttpHead
|
||||
location HttpUtils.h /^ char location[MAXURLLENGTH];\/\/ redirection$/;" m struct:HttpHead
|
||||
main main.cpp /^int main()$/;" f
|
||||
port HttpUtils.h /^ unsigned short port; \/\/ port$/;" m struct:HttpUrl
|
||||
request HttpUtils.h /^ char request[MAXURLLENGTH]; \/\/ request$/;" m struct:HttpUrl
|
||||
socket DownloadTask.h /^ TcpSocket* socket;$/;" m class:DownloadTask
|
||||
sockfd TcpSocket.h /^ int sockfd;$/;" m class:TcpSocket
|
||||
~DownloadTask DownloadTask.h /^ ~DownloadTask()$/;" f class:DownloadTask
|
||||
~Log Log.h /^ ~Log()$/;" f class:Log
|
@ -1 +0,0 @@
|
||||
[Oct-27-2012] fix a nonexitent address cause segmentation fault (core dumped)
|
@ -1,28 +0,0 @@
|
||||
#
|
||||
# "Makefile"
|
||||
# Copyright (C) <2012> <"Mark Deng" 2010.tpk@gmail.com>
|
||||
#
|
||||
# This program 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 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
SUBDIRS = src
|
||||
|
||||
subdirs:
|
||||
@for n in $(SUBDIRS); do $(MAKE) -C $$n || exit 1; done
|
||||
|
||||
clean:
|
||||
@for n in $(SUBDIRS); do $(MAKE) -C $$n clean; done
|
||||
@$(RM) download
|
||||
|
||||
debug:
|
||||
@for n in $(SUBDIRS); do $(MAKE) -C $$n debug; done
|
@ -1,11 +0,0 @@
|
||||
downloader
|
||||
|
||||
a simple http protocol download app, for learn linux c and socket program.
|
||||
|
||||
make
|
||||
./downloader [http file address]
|
||||
|
||||
make debug
|
||||
for debuging.
|
||||
|
||||
Mark Deng <2010.tpk@gmail.com>
|
@ -1,7 +0,0 @@
|
||||
find bugs:
|
||||
|
||||
3. a nonexitent address whill cause segmentation fault (core dumped)
|
||||
|
||||
2. can't parse path like this "hcd-1.imgbox.com/adbdxjtx.png?st=c86KOgBzkJbADDS5XzB_bQ&e=1351172248"
|
||||
|
||||
1. HTTP/1.1 302 Found can not reset download path.
|
Binary file not shown.
@ -1,32 +0,0 @@
|
||||
#
|
||||
# "Makefile"
|
||||
# Copyright (C) <2012> <"Mark Deng" 2010.tpk@gmail.com>
|
||||
#
|
||||
# This program 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 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -O2 -Wall
|
||||
objects = download.o http.o parse.o
|
||||
|
||||
all:download
|
||||
@mv download ..
|
||||
debug: CFLAGS += -g
|
||||
debug: download
|
||||
@mv download ..
|
||||
download:$(objects)
|
||||
$(CC) -o download $(objects) $(CFLAGS)
|
||||
|
||||
.PHONY:clean
|
||||
clean:
|
||||
$(RM) *.o
|
@ -1,27 +0,0 @@
|
||||
/*
|
||||
"dbg.h"
|
||||
Copyright (C) <2012> <"Mark Deng" 2010.tpk@gmail.com>
|
||||
|
||||
This program 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __DBG_H
|
||||
#define __DBG_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define CHECK_MEM(A) do { if (!(A)) { fprintf (stderr, "memery calloc error!"); goto error;} } while (0)
|
||||
|
||||
#endif /* __DBG_H */
|
@ -1,104 +0,0 @@
|
||||
/*
|
||||
"download.c"
|
||||
Copyright (C) <2012> <"Mark Deng" 2010.tpk@gmail.com>
|
||||
|
||||
This program 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "http.h"
|
||||
#include "parse.h"
|
||||
#include "download.h"
|
||||
#include "dbg.h"
|
||||
|
||||
static struct path * parse_path (char **argv);
|
||||
static void free_path (struct path *download_path);
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct path *download_path;
|
||||
struct ip_list *ips;
|
||||
if (argc != 2) exit (1);
|
||||
|
||||
download_path = parse_path (argv);
|
||||
|
||||
ips = parse_addr (download_path->hostname);
|
||||
|
||||
download (ips->ipstr, download_path->port, download_path->path, download_path->file_name);
|
||||
|
||||
free_path (download_path);
|
||||
free_ip_list (ips);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct path * parse_path (char **argv)
|
||||
{
|
||||
struct path *download_path = NULL;
|
||||
volatile int len;
|
||||
char *path_start = NULL;
|
||||
char *path_end = NULL;
|
||||
|
||||
len = strlen (argv[1]);
|
||||
|
||||
download_path = (struct path *) malloc (sizeof (struct path));
|
||||
if (download_path == NULL) exit (1);
|
||||
bzero (download_path, sizeof (struct path));
|
||||
|
||||
download_path->hostname = (char *) calloc ((size_t)len, sizeof (char));
|
||||
download_path->path = (char *) calloc ((size_t)len, sizeof (char));
|
||||
download_path->file_name = (char *) calloc ((size_t)len, sizeof(char));
|
||||
|
||||
CHECK_MEM(download_path->hostname);
|
||||
CHECK_MEM(download_path->path);
|
||||
CHECK_MEM(download_path->file_name);
|
||||
|
||||
path_start = strchr (argv[1], '/');
|
||||
path_end = strrchr (argv[1], '/');
|
||||
|
||||
if (path_start == NULL || path_end == NULL)
|
||||
{
|
||||
strncpy (download_path->hostname, argv[1], strlen (argv[1]));
|
||||
strncpy (download_path->path, "/", strlen ("/"));
|
||||
strncpy (download_path->file_name, "index.html", strlen ("index.html"));
|
||||
return download_path;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy (download_path->hostname, argv[1], len - strlen (path_start));
|
||||
memcpy (download_path->path, path_start, strlen (path_start) - strlen (path_end)+1);
|
||||
memcpy (download_path->file_name, path_end + 1, strlen (path_end));
|
||||
return download_path;
|
||||
}
|
||||
|
||||
|
||||
error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void free_path (struct path *download_path)
|
||||
{
|
||||
free (download_path->hostname);
|
||||
free (download_path->path);
|
||||
free (download_path->file_name);
|
||||
|
||||
download_path->hostname = NULL;
|
||||
download_path->path = NULL;
|
||||
download_path->file_name = NULL;
|
||||
|
||||
free (download_path);
|
||||
download_path = NULL;
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
/*
|
||||
"download.h"
|
||||
Copyright (C) <2012> <"Mark Deng" 2010.tpk@gmail.com>
|
||||
|
||||
This program 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __DOWNLOAD_H
|
||||
#define __DOWNLOAD_H
|
||||
|
||||
|
||||
struct path {
|
||||
char *hostname;
|
||||
char *path;
|
||||
char *file_name;
|
||||
int port;
|
||||
};
|
||||
|
||||
#endif /* __DOWNLOAD_H */
|
Binary file not shown.
@ -1,149 +0,0 @@
|
||||
/*
|
||||
"http.c"
|
||||
Copyright (C) <2012> <"Mark Deng" 2010.tpk@gmail.com>
|
||||
|
||||
This program 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "http.h"
|
||||
#include "dbg.h"
|
||||
|
||||
extern int download (char const *ipstr, int port, char const *path, char const *file_name)
|
||||
{
|
||||
int socketfd;
|
||||
char http_protol[BUFFER];
|
||||
int ret;
|
||||
float http_ver = 0.0;
|
||||
int status;
|
||||
int write_length;
|
||||
int file_len;
|
||||
char recvbuf[BUFFER]; /* recieves http server http protol HEAD */
|
||||
char buffer[BUFFER];
|
||||
FILE *fp = NULL;
|
||||
void *start = NULL;
|
||||
struct sockaddr_in download_addr;
|
||||
|
||||
if (port == 0) port = PORT;
|
||||
|
||||
if ((socketfd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
|
||||
{
|
||||
printf ("socket error:%d\n", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bzero (&download_addr, sizeof (download_addr));
|
||||
|
||||
download_addr.sin_family = AF_INET;
|
||||
download_addr.sin_port = htons (port);
|
||||
/* download_addr.sin_addr.s_addr = inet_addr("10.0.0.21"); */
|
||||
inet_pton (AF_INET, ipstr, &download_addr.sin_addr.s_addr);
|
||||
|
||||
if (connect (socketfd, (struct sockaddr *)&download_addr, (socklen_t)sizeof (download_addr)) == -1)
|
||||
{
|
||||
printf ("connect error:%d\n", errno);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
bzero (http_protol, sizeof (http_protol));
|
||||
bzero (recvbuf, sizeof (recvbuf));
|
||||
|
||||
sprintf (http_protol, "GET %s%s HTTP/1.1\n" \
|
||||
"From: 2010.tpk@gmail.com\n" \
|
||||
"Host: mark@\n" \
|
||||
"User-agent: downloadApp(beta-0.1, i386-pc-linux)\n" \
|
||||
"Conection: Keep-Alive\n\n",
|
||||
path, file_name);
|
||||
|
||||
|
||||
ret = write (socketfd, http_protol, strlen (http_protol));
|
||||
if (ret == -1)
|
||||
{
|
||||
printf ("write failed:%d\n", errno);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
ret = read (socketfd, recvbuf, sizeof (recvbuf));
|
||||
if (ret == 0)
|
||||
{
|
||||
printf ("server closed:%d\n", errno);
|
||||
exit (1);
|
||||
}
|
||||
else if (ret == -1)
|
||||
{
|
||||
printf ("read failed:%d\n", errno);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
printf ("%s", recvbuf);
|
||||
sscanf (strstr (recvbuf, "HTTP/"), "HTTP/%f %d", &http_ver, &status);
|
||||
sscanf (strstr (recvbuf, "Content-Length"), "Content-Length: %d", &file_len);
|
||||
|
||||
if (status != 200 || file_len == 0)
|
||||
{
|
||||
printf ("http connect failed!\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
|
||||
fp = fopen (file_name, "wb");
|
||||
if (fp == NULL)
|
||||
{
|
||||
printf ("File:%s Can not open:%d\n", file_name, errno);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
bzero (buffer, sizeof (buffer));
|
||||
|
||||
/* download file's address start here whithout http protol HEAD */
|
||||
start = (void *) strstr(recvbuf, "\r\n\r\n") + sizeof ("\r\n\r\n")-1;
|
||||
fwrite (start, sizeof (char), ret - ((void *)start - (void *)&recvbuf), fp);
|
||||
|
||||
while (1)
|
||||
{
|
||||
ret = read (socketfd, buffer, sizeof (buffer));
|
||||
|
||||
if (ret == 0) break; /* download finish */
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
printf ("Recieve data from server [%s] failed!\n", ipstr);
|
||||
break;
|
||||
}
|
||||
|
||||
write_length = fwrite (buffer, sizeof (char), ret, fp);
|
||||
if (write_length < ret)
|
||||
{
|
||||
printf ("File: %s write failed.\n", file_name);
|
||||
break;
|
||||
}
|
||||
bzero (buffer, sizeof (buffer));
|
||||
}
|
||||
|
||||
printf ("\ndownload %s file finish.\n", file_name);
|
||||
|
||||
close (socketfd);
|
||||
fclose (fp);
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
/*
|
||||
"http.h"
|
||||
Copyright (C) <2012> <"Mark Deng" 2010.tpk@gmail.com>
|
||||
|
||||
This program 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __HTTP_H
|
||||
#define __HTTP_H
|
||||
|
||||
#define PORT 80
|
||||
#define BUFFER 1024
|
||||
#define NAME_LEN 255
|
||||
|
||||
extern int download (char const *ipstr, int port, char const *path, char const *file_name);
|
||||
|
||||
#endif /* __HTTP_H */
|
Binary file not shown.
@ -1,112 +0,0 @@
|
||||
/*
|
||||
"parse.c"
|
||||
Copyright (C) <2012> <"Mark Deng" 2010.tpk@gmail.com>
|
||||
|
||||
This program 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "parse.h"
|
||||
#include "dbg.h"
|
||||
|
||||
static void add2list (struct ip_list * const ips,char const *ipstr);
|
||||
|
||||
extern struct ip_list *parse_addr (char const *hostname)
|
||||
{
|
||||
|
||||
struct addrinfo *answer, hint, *curr;
|
||||
char ipstr[16] = {'\0'};
|
||||
volatile int ret;
|
||||
|
||||
struct ip_list *ips = NULL;
|
||||
ips = (struct ip_list *) calloc (1, sizeof (struct ip_list));
|
||||
CHECK_MEM (ips);
|
||||
|
||||
bzero (&hint, sizeof (hint));
|
||||
hint.ai_family = AF_INET;
|
||||
hint.ai_socktype = SOCK_STREAM;
|
||||
|
||||
if ((ret = getaddrinfo (hostname, NULL, &hint, &answer)) != 0) {
|
||||
fprintf (stderr, "getaddrinfo: %s\n",gai_strerror (ret));
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for (curr = answer; curr != NULL; curr = curr->ai_next) {
|
||||
|
||||
inet_ntop (AF_INET,
|
||||
&(((struct sockaddr_in *)(curr->ai_addr))->sin_addr),
|
||||
ipstr, sizeof (ipstr));
|
||||
add2list (ips, ipstr);
|
||||
}
|
||||
|
||||
freeaddrinfo (answer);
|
||||
return ips;
|
||||
|
||||
error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
extern void free_ip_list (struct ip_list *ips)
|
||||
{
|
||||
struct ip_list *temp = NULL;
|
||||
struct ip_list *delete_temp = NULL;
|
||||
delete_temp = ips;
|
||||
|
||||
while (1)
|
||||
{
|
||||
temp = delete_temp->next;
|
||||
free (delete_temp);
|
||||
delete_temp = NULL;
|
||||
delete_temp = temp;
|
||||
if (delete_temp == NULL) break;
|
||||
}
|
||||
ips = NULL;
|
||||
}
|
||||
|
||||
static void add2list (struct ip_list * const ips,char const *ipstr)
|
||||
{
|
||||
struct ip_list *ip = NULL;
|
||||
struct ip_list *temp = NULL;
|
||||
|
||||
CHECK_MEM(ips);
|
||||
|
||||
if (ips->next == NULL && (strlen(ips->ipstr) == 0))
|
||||
{
|
||||
strncpy (ips->ipstr, ipstr, 16);
|
||||
return;
|
||||
}
|
||||
|
||||
ip = (struct ip_list *) calloc (1, sizeof (struct ip_list));
|
||||
CHECK_MEM (ip);
|
||||
|
||||
temp = ips;
|
||||
while (temp->next)
|
||||
{
|
||||
temp = temp->next;
|
||||
}
|
||||
temp->next = ip;
|
||||
strncpy (ip->ipstr, ipstr, 16);
|
||||
|
||||
error:
|
||||
return;
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
/*
|
||||
"parse.h"
|
||||
Copyright (C) <2012> <"Mark Deng" 2010.tpk@gmail.com>
|
||||
|
||||
This program 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __PARSE_H
|
||||
#define __PARSE_H
|
||||
|
||||
struct ip_list {
|
||||
char ipstr[16];
|
||||
struct ip_list *next;
|
||||
};
|
||||
|
||||
extern struct ip_list *parse_addr (char const *hostname);
|
||||
extern void free_ip_list (struct ip_list *ips);
|
||||
|
||||
#endif /* __PARSE_H */
|
Binary file not shown.
Binary file not shown.
@ -1,53 +0,0 @@
|
||||
PROJ_DIR = .
|
||||
INCLUDE_DIR = $(PROJ_DIR)/include
|
||||
SRC_DIR = $(PROJ_DIR)/src
|
||||
OBJ_DIR = $(PROJ_DIR)/obj
|
||||
|
||||
LIBFLAGS = -pthread
|
||||
|
||||
INCLUDE = -I $(INCLUDE_DIR)
|
||||
CFLAGS = -fno-builtin -ggdb -c -Wall $(LIBFLAGS) $(INCLUDE)
|
||||
CC = /usr/bin/gcc
|
||||
BIN = http_cache
|
||||
|
||||
|
||||
SRC = $(SRC_DIR)/http_cache.c \
|
||||
$(SRC_DIR)/listener.c \
|
||||
$(SRC_DIR)/tokenize.c \
|
||||
$(SRC_DIR)/file_parser.c \
|
||||
$(SRC_DIR)/http_config.c \
|
||||
$(SRC_DIR)/wcache.c
|
||||
|
||||
#OBJ = ${SRC:%.c=%.o}
|
||||
OBJ = $(OBJ_DIR)/http_cache.o \
|
||||
$(OBJ_DIR)/listener.o \
|
||||
$(OBJ_DIR)/tokenize.o \
|
||||
$(OBJ_DIR)/file_parser.o \
|
||||
$(OBJ_DIR)/http_config.o \
|
||||
$(OBJ_DIR)/wcache.o
|
||||
#---make targets
|
||||
all: $(BIN)
|
||||
#%.o: %.c
|
||||
# $(CC) $(CFLAGS) -c $< -o $(OBJ_DIR)/$@
|
||||
|
||||
$(OBJ_DIR)/http_cache.o: $(SRC_DIR)/http_cache.c
|
||||
$(CC) -c $(CFLAGS) $< -o $@
|
||||
|
||||
$(OBJ_DIR)/http_config.o: $(SRC_DIR)/http_config.c
|
||||
$(CC) -c $(CFLAGS) $< -o $@
|
||||
$(OBJ_DIR)/listener.o: $(SRC_DIR)/listener.c
|
||||
$(CC) -c $(CFLAGS) $< -o $@
|
||||
$(OBJ_DIR)/tokenize.o: $(SRC_DIR)/tokenize.c
|
||||
$(CC) -c $(CFLAGS) $< -o $@
|
||||
$(OBJ_DIR)/file_parser.o: $(SRC_DIR)/file_parser.c
|
||||
$(CC) -c $(CFLAGS) $< -o $@
|
||||
$(OBJ_DIR)/wcache.o: $(SRC_DIR)/wcache.c
|
||||
$(CC) -c $(CFLAGS) $< -o $@
|
||||
|
||||
http_cache: $(OBJ)
|
||||
$(CC) $(LIBFLAGS) -o $(OBJ_DIR)/$(BIN) $(OBJ)
|
||||
|
||||
.PHONY : clean
|
||||
clean:
|
||||
rm -f $(OBJ_DIR)/*.o
|
||||
rm -f $(OBJ_DIR)/http_cache
|
@ -1,69 +0,0 @@
|
||||
*****Simple HTTP Web Proxy cache*****
|
||||
*************************************
|
||||
Author: Gaurav Tungatkar
|
||||
|
||||
Description:
|
||||
A HTTP proxy. Point your browser to the proxy. It fetches the pages from the
|
||||
actual server and caches them. It follows the HTTP RFC regarding keeping the
|
||||
cache updated.
|
||||
If the http response contains if-modified-since field, a conditional get is sent
|
||||
to determine the liveness of the cached object. If it does not contain
|
||||
if-modified-since, simple fiexd timeout strategy is used.
|
||||
|
||||
Handy doubly linked list utility created, reusable in other projects.
|
||||
|
||||
Code organization and directory structure:
|
||||
|
||||
Project3
|
||||
|__ src - All .c source files in this directory
|
||||
|__ include - All header files in this directory
|
||||
|_Makefile, config.txt - These files at the same level
|
||||
|
||||
tokenize.c -utility function to separate a line into tokens
|
||||
fileparser.c -utility function to parse a file line by line
|
||||
http_config.c -Handles reading of config file, error checking and populating
|
||||
in-memory config object.
|
||||
http_cache.c -Main http web cache engine, acts as client as well as server
|
||||
listener.c -Connection handler routine
|
||||
|
||||
Instructions to compile:
|
||||
1. Go to base project folder Project3.
|
||||
2. make
|
||||
|
||||
Instructions to run:
|
||||
1. From Project3 folder
|
||||
./obj/http_server [config_file]
|
||||
|
||||
Config file path is optional. It may be specified at the command line.
|
||||
If not provided, following assumptions hold:-
|
||||
a. config.txt located in Project1 directory is the default config file
|
||||
b. The config.txt file must be located in the cwd(current working
|
||||
directory from which server is run)
|
||||
|
||||
Cache Replacement:
|
||||
Modified FIFO - The cache follows a modified First-In-first-out policy. Lets say
|
||||
cache is full. New entry has size s. The first entry in FIFO order which is of
|
||||
size greater than s, is removed. Hence, now the cache will have sufficent space
|
||||
to add new entry.
|
||||
|
||||
Cache Design -
|
||||
Cache - A doubly linked list which stores all cache entries.
|
||||
|
||||
Hashtable - An array of list pointers, indexed by the hash index, pointing to
|
||||
all elements hashing to that value. So any element can be looked up directly
|
||||
based on its hash value of its http header.
|
||||
|
||||
Cache entry - Each entry stores a list of fragments - data buffers linked
|
||||
together which form part of the object.
|
||||
|
||||
Cache stale entries - Cache objects are updated lazily. No timer is used. Only when a
|
||||
particular entry is accessed, it is checked for staleness. If stale, a request
|
||||
to original server is sent and new data is cached.
|
||||
|
||||
Limitations:
|
||||
Client may send requests in HTTP/1.0 or HTTP/1.1 version. However, server response is always HTTP/1.0
|
||||
This folows the Robustness principle given in rfc 2145 http://www.ietf.org/rfc/rfc2145.txt
|
||||
|
||||
Logging:
|
||||
Logs can be turned off by defining TRACE to be 0 in log.h at compile time.
|
||||
All logs are currently directed to stdout.
|
@ -1,6 +0,0 @@
|
||||
LISTEN 8080
|
||||
#comment
|
||||
#DocumentRoot /home/gaurav/Dropbox/Code
|
||||
CACHESIZE 20000000
|
||||
CACHETIME 60
|
||||
IPADDR 10.2.69.8
|
@ -1,5 +0,0 @@
|
||||
#ifndef __FILE_PARSER_HDR__
|
||||
#define __FILE_PARSER_HDR__
|
||||
int file_parser(char *filename, int (*reader)(void *, char *line), void *c);
|
||||
#define MAX_LINE 1024
|
||||
#endif
|
@ -1,42 +0,0 @@
|
||||
#ifndef __HTTPCONF__
|
||||
#define __HTTPCONF__
|
||||
|
||||
#define MAX_BUF 1024
|
||||
#define PENDING_CONN 64
|
||||
|
||||
#include "log.h"
|
||||
|
||||
/* various config element types parsed from config file*/
|
||||
enum cfg_elements_t {
|
||||
CACHE_SIZE,
|
||||
CACHE_TIME,
|
||||
IPADDR,
|
||||
LISTEN_PORT,
|
||||
DEFAULT
|
||||
};
|
||||
struct http_server_config {
|
||||
int listen_port; /*server listens on this port*/
|
||||
char ipaddr[16];
|
||||
int cachesize;
|
||||
int cachetime;
|
||||
};
|
||||
|
||||
/*Not used currently. Convenience data structure in case threads need to be used*/
|
||||
struct http_server_data {
|
||||
struct http_server_config *cfg;
|
||||
int sockfd;
|
||||
};
|
||||
|
||||
/*Function declarations */
|
||||
|
||||
int cfg_reader(void *c, char *line);
|
||||
void http_server(void * sockfd);
|
||||
|
||||
int valid_method_string(char **request, char *request_method);
|
||||
int valid_version(char **request, struct http_server_config *cfg,
|
||||
char *version);
|
||||
|
||||
int valid_uri(char **request, struct http_server_config *cfg,
|
||||
char *uri);
|
||||
int connection_handler(struct http_server_config *cfg);
|
||||
#endif
|
@ -1,12 +0,0 @@
|
||||
#ifndef __LOGGER__
|
||||
#define __LOGGER__
|
||||
#ifndef TRACE
|
||||
#define TRACE 0
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
#define LOG(_hdl, _msg) \
|
||||
{ \
|
||||
if(TRACE > 0) \
|
||||
fprintf(_hdl,"Pid=%d:%s %s",getpid(),__FUNCTION__, _msg); \
|
||||
}
|
||||
#endif
|
@ -1,7 +0,0 @@
|
||||
#ifndef __TOKENIZE_HDR__
|
||||
#define __TOKENIZE_HDR__
|
||||
#define MAX_TOKEN_SIZE 2048
|
||||
|
||||
int tokenize(char **str, char *dest);
|
||||
|
||||
#endif
|
@ -1,82 +0,0 @@
|
||||
#ifndef __WCACHE_HDR__
|
||||
#define __WCACHE_HDR__
|
||||
#include <stddef.h>
|
||||
#include <pthread.h>
|
||||
#define WCACHE_HASHTABLE_SIZE 1024
|
||||
#define HTTP_SIGNATURE_SIZE 512
|
||||
#define HTTP_SIGNATURE_HASH_SIZE 32
|
||||
#define CACHETIME 6
|
||||
#define FRAGMENT_SIZE 4096
|
||||
#define TIMESTAMP_SIZE 50
|
||||
#define OK 1
|
||||
#define ERROR 0
|
||||
|
||||
struct wcache_list_element {
|
||||
struct wcache_list_element *next;
|
||||
struct wcache_list_element *prev;
|
||||
};
|
||||
|
||||
|
||||
/* New linked list*/
|
||||
struct wcache_list {
|
||||
struct wcache_list_element head;
|
||||
};
|
||||
|
||||
/*A cache storing a list of cache objects*/
|
||||
struct wcache {
|
||||
int curr_size;
|
||||
int max_size;
|
||||
struct wcache_list l;
|
||||
};
|
||||
|
||||
/*A single entry in cache*/
|
||||
struct wcache_entry {
|
||||
char http_signature[HTTP_SIGNATURE_SIZE];
|
||||
unsigned int signature_hash;
|
||||
time_t ts;
|
||||
char timestamp[TIMESTAMP_SIZE];
|
||||
int last_modified; /*did this entry have a Last-modified parameter*/
|
||||
int valid; /*Is this entry valid*/
|
||||
int size; /*size of the entry*/
|
||||
struct wcache_list fragments; /*list storing fragment buffers*/
|
||||
struct wcache_list_element cache_elem; /*part of single global cache
|
||||
list*/
|
||||
struct wcache_list_element hash_elem; /*part of hash table*/
|
||||
pthread_mutex_t entry_lock;
|
||||
};
|
||||
struct fragment_buffer {
|
||||
char buffer[FRAGMENT_SIZE];
|
||||
int size;
|
||||
struct wcache_list_element elem;
|
||||
};
|
||||
|
||||
|
||||
#define container_of(ptr, type, member) ({ \
|
||||
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
|
||||
(type *)( (char *)__mptr - offsetof(type,member) );})
|
||||
|
||||
|
||||
#define list_for_each(pos, list) \
|
||||
for (pos = ((list)->head.next); pos != &((list)->head)&&(pos!= NULL); pos = pos->next)
|
||||
|
||||
int wcache_list_add(struct wcache_list *l, struct wcache_list_element *e);
|
||||
struct wcache_entry * wcache_entry_alloc();
|
||||
void wcache_list_init(struct wcache_list *l);
|
||||
int wcache_list_del(struct wcache_list_element *e);
|
||||
|
||||
int wcache_fragment_add(struct wcache_entry *entry, char *buffer, int size);
|
||||
int wcache_entry_replace(struct wcache *w, struct wcache_entry *entry);
|
||||
|
||||
int wcache_add(struct wcache *w, struct wcache_entry *entry);
|
||||
struct wcache_entry * wcache_find(char http_signature[HTTP_SIGNATURE_SIZE],
|
||||
time_t ts);
|
||||
|
||||
void wcache_table_init();
|
||||
int wcache_remove_entry(struct wcache_entry *w);
|
||||
struct wcache_entry * wcache_find(char http_signature[HTTP_SIGNATURE_SIZE],
|
||||
time_t ts);
|
||||
int wcache_remove_fragments(struct wcache_entry *w);
|
||||
unsigned int hash(char * buf);
|
||||
int lock_entry(struct wcache_entry *w);
|
||||
int unlock_entry(struct wcache_entry *w);
|
||||
#endif
|
@ -1,14 +0,0 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!DOCTYPE html
|
||||
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US"
|
||||
xml:lang="en-US"><head><title>Gaurav Tungatkar</title>
|
||||
</head><body vlink="#660000" text="#000000" bgcolor="#FFFFFF" link="#CC0000">
|
||||
<p>Under construction!</p>
|
||||
<IMG SRC="trophies.jpg"
|
||||
HEIGHT=275 ALT="Photo">
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,13 +0,0 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>.: Game Land :. © Graformix</title>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
JUST A TEST FILE!
|
||||
</body>
|
||||
</html>
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,50 +0,0 @@
|
||||
/*
|
||||
* File Name : file_parser.c
|
||||
* Author : Gaurav Tungatkar
|
||||
* Creation Date : 16-01-2011
|
||||
* Description :
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "fileparser.h"
|
||||
|
||||
/* file_parser() - Parse a file line by line
|
||||
* Parameters:
|
||||
* filename - name of file to parse
|
||||
* reader - user defined routine to handle the parsed line. It is fed the
|
||||
* current parsed line
|
||||
* c - void* user defined data that will be passed to reader()
|
||||
*/
|
||||
int file_parser(char *filename, int (*reader)(void *, char *line), void *c)
|
||||
{
|
||||
|
||||
FILE *fp;
|
||||
char line[MAX_LINE];
|
||||
assert(filename != NULL);
|
||||
assert(c != NULL);
|
||||
if((fp = fopen(filename, "r")) == NULL)
|
||||
{
|
||||
LOG(stdout, "Failed to open config file\n");
|
||||
return -1;
|
||||
}
|
||||
while((fgets(line, MAX_LINE, fp) != NULL))
|
||||
{
|
||||
if(reader(c, line) == -1)
|
||||
{
|
||||
LOG(stdout, "file parser: reader() failed\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
return 0;
|
||||
|
||||
}
|
@ -1,561 +0,0 @@
|
||||
/*
|
||||
* File Name : http_server.c
|
||||
* Author : Gaurav Tungatkar
|
||||
* Creation Date : 17-01-2011
|
||||
* Description :
|
||||
*
|
||||
*/
|
||||
//needed for strptime
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <netdb.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include "httpconf.h"
|
||||
#include "wcache.h"
|
||||
#include "tokenize.h"
|
||||
#include "fileparser.h"
|
||||
|
||||
|
||||
#define BUFFSIZE (4*1024)
|
||||
#define MAX_FILENAME 512
|
||||
#define HTTP_HDR_SIZE 512
|
||||
#define HTTP_URI_SIZE 2048
|
||||
#define HTTP_STATUS_SIZE 1024
|
||||
#define HTTP_GET 11
|
||||
#define HTTP_PORT 80
|
||||
#define SP 0x20
|
||||
#define CRLF "\r\n"
|
||||
|
||||
struct http_server_config cfg ;
|
||||
struct wcache cache;
|
||||
pthread_mutex_t list_mutex;
|
||||
|
||||
/*Check if the request contains the Last-modified field.
|
||||
*return time in seconds since epoch if yes.
|
||||
*return 0 if it doesnt exist*/
|
||||
time_t get_last_modified(char * request, char *timestamp)
|
||||
{
|
||||
char *str;
|
||||
char dummy[20];
|
||||
struct tm t;
|
||||
time_t tsec = 0;
|
||||
int i = 0;
|
||||
assert(request);
|
||||
|
||||
timestamp[0] = '\0';
|
||||
str = strstr(request, "Last-Modified:");
|
||||
if(str == NULL) goto NOTIME;
|
||||
if(tokenize(&str, dummy) <= 0)
|
||||
goto NOTIME;
|
||||
while(*str == ' ')
|
||||
str++;
|
||||
//if(tokenize(&str, timestr) <= 0)
|
||||
// goto NOTIME;
|
||||
while(str[i] != '\n')
|
||||
{
|
||||
timestamp[i] = str[i];
|
||||
i++;
|
||||
}
|
||||
timestamp[i] = '\n';
|
||||
timestamp[i+1] = '\0';
|
||||
if (strptime(timestamp, "%a,%n%e%n%b%n%Y%n%H:%M:%S", &t) != 0 )
|
||||
tsec = mktime(&t);
|
||||
else
|
||||
goto NOTIME;
|
||||
return tsec;
|
||||
|
||||
NOTIME:
|
||||
return 0;
|
||||
}
|
||||
|
||||
int valid_method_string(char **request, char *request_method)
|
||||
{
|
||||
/*only GET method supported for caching */
|
||||
if((tokenize(request, request_method) != 3)
|
||||
||(strcmp(request_method, "GET") != 0))
|
||||
{
|
||||
// LOG(stdout, "Invalid method\n");
|
||||
// return -1;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return HTTP_GET;
|
||||
}
|
||||
|
||||
}
|
||||
int valid_version(char **request, struct http_server_config *cfg,
|
||||
char *version)
|
||||
{
|
||||
/* HTTP versions 1.0 and 1.1 messages are accepted
|
||||
*/
|
||||
if((tokenize(request, version) <= 0)
|
||||
||((strcmp(version, "HTTP/1.1") != 0) && (strcmp(version,
|
||||
"HTTP/1.0") != 0)))
|
||||
{
|
||||
LOG(stdout, "Version not supported\n");
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
int valid_uri(char **request, struct http_server_config *cfg,
|
||||
char *uri)
|
||||
{
|
||||
/*if it sees 2 or more leading spaces(SP) - thats invalid URI*/
|
||||
if(*(*(request)+1) == SP)
|
||||
{
|
||||
LOG(stdout, "Invalid URI\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if((tokenize(request, uri) <= 0))
|
||||
{
|
||||
LOG(stdout, "Invalid URI\n");
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
//cannot refer to the parent directory
|
||||
if(uri[0] == '.' && uri[1] == '.')
|
||||
{
|
||||
LOG(stdout, "Invalid URI\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/*Given a host name and a port, open a socket to that host.
|
||||
*Returns that socket*/
|
||||
int connect_server(char *host, int port)
|
||||
{
|
||||
/* refer to getaddrinfo() man page */
|
||||
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *res, *rp;
|
||||
int fd, s;
|
||||
int flag = 0;
|
||||
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = 0;
|
||||
hints.ai_protocol = 0;
|
||||
hints.ai_canonname = NULL;
|
||||
hints.ai_addr = NULL;
|
||||
hints.ai_next = NULL;
|
||||
|
||||
s = getaddrinfo(host, NULL, &hints, &res);
|
||||
if (s != 0) {
|
||||
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
|
||||
return ERROR;
|
||||
}
|
||||
for (rp = res; rp != NULL; rp = rp->ai_next) {
|
||||
if(rp->ai_socktype != SOCK_STREAM)
|
||||
continue;
|
||||
fd = socket(rp->ai_family, rp->ai_socktype,
|
||||
rp->ai_protocol);
|
||||
if (fd == -1)
|
||||
continue;
|
||||
if(port >= 0)
|
||||
((struct sockaddr_in *)(rp->ai_addr))->sin_port =
|
||||
htons(port);
|
||||
else
|
||||
((struct sockaddr_in *)(rp->ai_addr))->sin_port =
|
||||
htons(HTTP_PORT);
|
||||
if (connect(fd, rp->ai_addr, rp->ai_addrlen) == 0)
|
||||
{
|
||||
flag = 1;
|
||||
break; /* Success */
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
freeaddrinfo(res);
|
||||
|
||||
if (!flag) { /* No address succeeded */
|
||||
fprintf(stderr, "Could not bind\n");
|
||||
return ERROR;
|
||||
}
|
||||
return fd;
|
||||
|
||||
}
|
||||
|
||||
/*Get the host name from the HTTP request buffer*/
|
||||
int get_host(char *request, char *host)
|
||||
{
|
||||
char token[HTTP_URI_SIZE];
|
||||
while(tokenize(&request, token) > 0)
|
||||
{
|
||||
if(!strcasecmp(token, "Host:"))
|
||||
break;
|
||||
}
|
||||
if(tokenize(&request, host) <= 0)
|
||||
return ERROR;
|
||||
return OK;
|
||||
|
||||
}
|
||||
|
||||
int get_port(char *request)
|
||||
{
|
||||
int r;
|
||||
while((*request)!= '\n' &&
|
||||
(*request)!='\0' &&
|
||||
(*request != ':'))
|
||||
request++;
|
||||
if(*request == ':')
|
||||
request++;
|
||||
r = strtol(request, NULL, 10);
|
||||
if(r > 0 && r < 65535)
|
||||
return r;
|
||||
return -1;
|
||||
}
|
||||
/* Connect to the original server and get the response.
|
||||
* Cache the object.*/
|
||||
int get_fresh_response_and_cache(char *request, char *uri, int cfd)
|
||||
{
|
||||
struct wcache_entry *entry = wcache_entry_alloc();
|
||||
char host[1024]; //original server
|
||||
char buff[FRAGMENT_SIZE];
|
||||
char timestamp[100];
|
||||
int fd;
|
||||
time_t m;
|
||||
int rlen = 0, sent = 0;
|
||||
int nbytes;
|
||||
struct timeval t;
|
||||
gettimeofday(&t, NULL);
|
||||
|
||||
//TODO dynamically allocate the signature buffer
|
||||
memcpy(entry->http_signature, uri, sizeof(entry->http_signature));
|
||||
|
||||
|
||||
//strcpy(entry->http_request, request);
|
||||
|
||||
entry->valid = 1;
|
||||
entry->signature_hash = hash(uri);
|
||||
entry->size = sizeof(struct wcache_entry);
|
||||
entry->ts = t.tv_sec;
|
||||
wcache_list_init(&entry->fragments);
|
||||
LOG(stdout,"\nNew cache entry created for uri :");
|
||||
LOG(stdout, uri);
|
||||
|
||||
get_host(request, host); //TODO
|
||||
fd = connect_server(host, HTTP_PORT);
|
||||
// FIXME
|
||||
rlen = strlen(request);
|
||||
while(sent < rlen)
|
||||
{
|
||||
nbytes = write(fd, request+sent, rlen - sent);
|
||||
if(nbytes == -1){
|
||||
perror("Socket write failed");
|
||||
exit(1);
|
||||
}
|
||||
sent += nbytes;
|
||||
}
|
||||
|
||||
/*sent the request to the actual server.
|
||||
*Now receive the response, fill up the cache entry
|
||||
*/
|
||||
int f = 0;
|
||||
while((rlen = read(fd, buff, FRAGMENT_SIZE)) > 0)
|
||||
{
|
||||
if(f == 0)
|
||||
{
|
||||
f = 1;
|
||||
m = get_last_modified(buff, timestamp);
|
||||
if(m != 0)
|
||||
{
|
||||
memcpy(entry->timestamp, timestamp, sizeof(entry->timestamp));
|
||||
entry->last_modified = 1;
|
||||
entry->ts = m ;
|
||||
}
|
||||
else
|
||||
entry->last_modified = 0;
|
||||
}
|
||||
wcache_fragment_add(entry, buff, rlen);
|
||||
write(cfd, buff, rlen);
|
||||
}
|
||||
wcache_add(&cache, entry);
|
||||
return OK;
|
||||
|
||||
}
|
||||
/* Check response. If it contains 304: Not modified, return 1; else return 0*/
|
||||
int not_modified(char *buffer)
|
||||
{
|
||||
if(strstr(buffer, "304") != NULL)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*Ignore SIGPIPE */
|
||||
void sigpipe_ign()
|
||||
|
||||
{
|
||||
struct sigaction act;
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigemptyset (&act.sa_mask);
|
||||
act.sa_flags = 0;
|
||||
sigaction (SIGPIPE, &act, NULL);
|
||||
|
||||
|
||||
}
|
||||
void http_server(void * data)
|
||||
{
|
||||
|
||||
char request[BUFFSIZE+1];
|
||||
int numbytes;
|
||||
int errflag = 0;
|
||||
char request_method[5];
|
||||
char version[10];
|
||||
int method;
|
||||
int sockfd;
|
||||
char uri[HTTP_URI_SIZE];
|
||||
char status[HTTP_STATUS_SIZE];
|
||||
sockfd = *(int *)data;
|
||||
// free(data);
|
||||
|
||||
if((numbytes = read(sockfd, (void *)request, BUFFSIZE)) <= 0)
|
||||
{
|
||||
LOG(stdout, "read from socket failed");
|
||||
close(sockfd);
|
||||
return;
|
||||
}
|
||||
char *requestptr = request;
|
||||
if((method = valid_method_string(&requestptr, request_method)) == -1)
|
||||
{
|
||||
//ERROR in Request
|
||||
snprintf(status,
|
||||
HTTP_STATUS_SIZE,
|
||||
"HTTP/1.0 400 Bad Request: Invalid Method: %s\r\n",
|
||||
request_method);
|
||||
LOG(stdout, status);
|
||||
errflag = 1;
|
||||
}
|
||||
if(method != HTTP_GET)
|
||||
{
|
||||
int sfd, rlen, n;
|
||||
char host[1024]; //original server
|
||||
char *r = request;
|
||||
LOG(stdout, "Acting as relay. Caching only for GET\n");
|
||||
get_host(r, host); //TODO
|
||||
//port = get_port(r);
|
||||
sfd = connect_server(host, HTTP_PORT);
|
||||
n = write(sfd, request, BUFFSIZE);
|
||||
while((rlen = read(sfd, request, BUFFSIZE)) > 0)
|
||||
{
|
||||
write(sockfd, request, rlen);
|
||||
}
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if(!errflag && (method == HTTP_GET))
|
||||
{
|
||||
//tokenize URI
|
||||
//check that the method name and URI are separated by exactly 1
|
||||
//SP character
|
||||
//requestptr should now be pointing at a SP character. If the
|
||||
//next character is SP as well, invalid request
|
||||
if(valid_uri(&requestptr, &cfg, uri) == -1)
|
||||
{
|
||||
snprintf(status,
|
||||
HTTP_STATUS_SIZE,
|
||||
"HTTP/1.0 400 Bad Request: Invalid URI: %s\r\n",
|
||||
uri);
|
||||
LOG(stdout, status);
|
||||
//ERROR in request
|
||||
errflag = 1;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
if(!errflag)
|
||||
{
|
||||
if(valid_version(&requestptr, &cfg, version) == -1)
|
||||
{
|
||||
//ERROR
|
||||
//HTTP/1.0 400 Bad Request: Invalid HTTP-Version:
|
||||
//<requested HTTP version>
|
||||
snprintf(status,
|
||||
HTTP_STATUS_SIZE,
|
||||
"HTTP/1.0 400 Bad Request: Invalid HTTP-Version: %s\r\n",
|
||||
version);
|
||||
LOG(stdout, status);
|
||||
errflag = 1;
|
||||
}
|
||||
|
||||
}
|
||||
//seems like request came up fine! Now lets see if we can read the file
|
||||
if(!errflag)
|
||||
{
|
||||
struct wcache_entry *entry;
|
||||
struct timeval tv;
|
||||
struct wcache_list_element *e;
|
||||
struct fragment_buffer *fb;
|
||||
time_t ctim;
|
||||
char cond_get[BUFFSIZE] = {0};
|
||||
int rlen;
|
||||
char host[1024]; //original server
|
||||
// get if-modified-since ts here TODO
|
||||
gettimeofday(&tv, NULL);
|
||||
entry = wcache_find(uri, tv.tv_sec);
|
||||
if(entry)
|
||||
{
|
||||
lock_entry(entry);
|
||||
if(entry->last_modified)
|
||||
{
|
||||
//construct conditional GET request, send it to
|
||||
//server. If response says not modified, return
|
||||
//the cache value.
|
||||
//Else discard the old fragments and add the
|
||||
//newly returned ones.
|
||||
//conditional GET
|
||||
int srvfd;
|
||||
snprintf(cond_get,BUFFSIZE,
|
||||
"GET %s HTTP/1.0\r\nIf-modified-since: %s\n",
|
||||
uri, entry->timestamp);
|
||||
char *rr = request;
|
||||
get_host(rr, host); //TODO
|
||||
srvfd = connect_server(host, HTTP_PORT);
|
||||
if(write(srvfd, cond_get, BUFFSIZE) <= 0)
|
||||
{
|
||||
LOG(stdout, "socket write error\n");
|
||||
}
|
||||
cond_get[0] = '\0';
|
||||
LOG(stdout, "Sending conditional GET:");
|
||||
LOG(stdout, host);
|
||||
//now just get the initial part of the request
|
||||
//check if it is 304 Not-Modified
|
||||
rlen = read(srvfd, cond_get, FRAGMENT_SIZE);
|
||||
if(not_modified(cond_get))
|
||||
{
|
||||
LOG(stdout, "Not modified. Return cached result\n");
|
||||
list_for_each(e, &(entry->fragments))
|
||||
{
|
||||
fb = container_of(e, struct fragment_buffer, elem);
|
||||
write(sockfd, fb->buffer, fb->size);
|
||||
}
|
||||
close(sockfd);
|
||||
unlock_entry(entry);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
//remove the old fragments and cache new
|
||||
//ones
|
||||
|
||||
wcache_remove_fragments(entry);
|
||||
ctim = get_last_modified(cond_get,
|
||||
entry->timestamp);
|
||||
if(ctim != 0)
|
||||
{
|
||||
entry->last_modified = 1;
|
||||
entry->ts = ctim ;
|
||||
}
|
||||
else
|
||||
{
|
||||
entry->last_modified = 0;
|
||||
entry->ts = tv.tv_sec;
|
||||
}
|
||||
wcache_fragment_add(entry, cond_get, rlen);
|
||||
write(sockfd, cond_get, rlen);
|
||||
while((rlen = read(srvfd, cond_get, FRAGMENT_SIZE)) > 0)
|
||||
{
|
||||
wcache_fragment_add(entry,
|
||||
cond_get, rlen);
|
||||
write(sockfd, cond_get, rlen);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//FOUND IN CACHE!
|
||||
if((tv.tv_sec - entry->ts) <= cfg.cachetime)
|
||||
{
|
||||
list_for_each(e, &(entry->fragments))
|
||||
{
|
||||
fb = container_of(e, struct fragment_buffer, elem);
|
||||
write(sockfd, fb->buffer, fb->size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//cache entry has expired. This is lazy
|
||||
//checking of expired entries.
|
||||
entry->valid = 0;
|
||||
unlock_entry(entry);
|
||||
wcache_remove_entry(entry);
|
||||
get_fresh_response_and_cache(request, uri, sockfd);
|
||||
goto NOUNLOCK;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//NOT IN CACHE TODO
|
||||
get_fresh_response_and_cache(request, uri, sockfd);
|
||||
|
||||
}
|
||||
if(entry != NULL)
|
||||
unlock_entry(entry);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
NOUNLOCK:
|
||||
close(sockfd);
|
||||
return;
|
||||
}
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
char filename[MAX_FILENAME];
|
||||
sigpipe_ign();
|
||||
if(argc == 2)
|
||||
{
|
||||
if(strlen(argv[1]) < MAX_FILENAME)
|
||||
strcpy(filename, argv[1]);
|
||||
}
|
||||
else
|
||||
strcpy(filename, "config.txt");
|
||||
pthread_mutex_init(&list_mutex, NULL);
|
||||
wcache_list_init(&(cache.l));
|
||||
if(file_parser(filename, cfg_reader, &cfg)== -1)
|
||||
{
|
||||
LOG(stdout, "Configuration Error.Exiting...\n");
|
||||
exit(1);
|
||||
|
||||
}
|
||||
cache.max_size = (cfg.cachesize)*(1024);
|
||||
LOG(stdout,
|
||||
"Starting primitive HTTP web server implemented for CSC573\n");
|
||||
wcache_table_init();
|
||||
if(connection_handler(&cfg) == -1)
|
||||
{
|
||||
LOG(stdout, "Error!Exiting..\n");
|
||||
exit(1);
|
||||
}
|
||||
pthread_mutex_destroy(&list_mutex);
|
||||
return 0;
|
||||
}
|
@ -1,147 +0,0 @@
|
||||
/*
|
||||
* File Name : http_config.c
|
||||
* Author : Gaurav Tungatkar
|
||||
* Creation Date : 14-01-2011
|
||||
* Description :
|
||||
*
|
||||
*/
|
||||
/*Read http server configuration from the config file and store it in memory
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "log.h"
|
||||
#include "tokenize.h"
|
||||
#include "httpconf.h"
|
||||
#include "fileparser.h"
|
||||
|
||||
int cfg_reader(void *c, char *line)
|
||||
{
|
||||
assert(c);
|
||||
assert(line);
|
||||
char token[MAX_TOKEN_SIZE];
|
||||
int next_token = DEFAULT;
|
||||
struct http_server_config *cfg = (struct http_server_config *)c;
|
||||
int config_cnt = 0;
|
||||
/* From the given config file format, we assume we have 4 distinct
|
||||
* config objects - port, DocumentRoot, DirectoryIndex, supported files
|
||||
* and all are mandatory. If one of them is missing, we should give an
|
||||
* error and return.
|
||||
* This function gets one line of the config file at a time, which is
|
||||
* further parses.
|
||||
*/
|
||||
|
||||
while(tokenize(&line, token) > 0)
|
||||
{
|
||||
if(token[0] == '#')
|
||||
{
|
||||
//This line must be a Comment. Ignore and return
|
||||
return 0;
|
||||
}
|
||||
if(strcmp(token, "IPADDR") == 0)
|
||||
{
|
||||
next_token = IPADDR;
|
||||
config_cnt++;
|
||||
|
||||
|
||||
}
|
||||
else if(strcmp(token, "LISTEN") == 0)
|
||||
{
|
||||
next_token = LISTEN_PORT;
|
||||
config_cnt++;
|
||||
|
||||
}
|
||||
else if(strcmp(token, "CACHESIZE") == 0)
|
||||
{
|
||||
next_token = CACHE_SIZE;
|
||||
config_cnt++;
|
||||
}
|
||||
else if(strcmp(token, "CACHETIME") == 0)
|
||||
{
|
||||
next_token = CACHE_TIME;
|
||||
config_cnt++;
|
||||
}
|
||||
else
|
||||
{
|
||||
int len;
|
||||
char *p;
|
||||
len = strlen(token);
|
||||
/* next_token was set previously based on the type of
|
||||
* config object. Based on that type, we now store its
|
||||
* value
|
||||
*/
|
||||
switch(next_token)
|
||||
{
|
||||
case CACHE_SIZE:
|
||||
if(len > 10)
|
||||
{
|
||||
LOG(stdout,
|
||||
"config: cache size exceeded\n");
|
||||
return -1;
|
||||
|
||||
}
|
||||
cfg->cachesize = strtol(token, &p, 10);
|
||||
config_cnt++;
|
||||
next_token = DEFAULT;
|
||||
break;
|
||||
case LISTEN_PORT:
|
||||
cfg->listen_port = strtol(token, &p, 10);
|
||||
if(cfg->listen_port > 65535)
|
||||
{
|
||||
LOG(stdout,
|
||||
"Port value invalid\n");
|
||||
return -1;
|
||||
}
|
||||
config_cnt++;
|
||||
next_token = DEFAULT;
|
||||
break;
|
||||
case CACHE_TIME:
|
||||
cfg->cachetime = strtol(token, &p, 10);
|
||||
if(cfg->cachetime > 65535)
|
||||
{
|
||||
LOG(stdout,
|
||||
"time value invalid\n");
|
||||
return -1;
|
||||
}
|
||||
config_cnt++;
|
||||
next_token = DEFAULT;
|
||||
break;
|
||||
case IPADDR:
|
||||
if(len >
|
||||
sizeof(cfg->ipaddr))
|
||||
{
|
||||
|
||||
LOG(stdout,
|
||||
"config:IP addr size exceeded\n");
|
||||
return -1;
|
||||
|
||||
}
|
||||
strcpy(cfg->ipaddr,token);
|
||||
config_cnt++;
|
||||
next_token = DEFAULT;
|
||||
break;
|
||||
default:
|
||||
LOG(stdout,
|
||||
"Error in config file.Exiting...\n");
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
/* config_cnt counts how many config types or values we have seen.
|
||||
* if it is 1, it means we just got the type and not the value.*/
|
||||
if(config_cnt == 1)
|
||||
{
|
||||
LOG(stdout, "Error in config file\n");
|
||||
return -1;
|
||||
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -1,111 +0,0 @@
|
||||
/*
|
||||
* File Name : listener.c
|
||||
* Author : Gaurav Tungatkar
|
||||
* Creation Date : 16-01-2011
|
||||
* Description :
|
||||
*
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "httpconf.h"
|
||||
|
||||
|
||||
int connection_handler(struct http_server_config *cfg)
|
||||
{
|
||||
int listen_fd, new_fd, set = 1, rc;
|
||||
struct sockaddr_in listener_addr, client_addr;
|
||||
socklen_t addr_len = 0;
|
||||
pthread_t thr;
|
||||
char p[50];
|
||||
int *tdata = NULL;
|
||||
assert(cfg != NULL);
|
||||
/* Standard server side socket sequence*/
|
||||
|
||||
if((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
|
||||
{
|
||||
LOG(stdout, "socket() failure\n");
|
||||
return -1;
|
||||
}
|
||||
if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &set,
|
||||
sizeof(int)) == -1) {
|
||||
LOG(stdout, "setsockopt() failed");
|
||||
return -1;
|
||||
}
|
||||
bzero(&listener_addr, sizeof(listener_addr));
|
||||
listener_addr.sin_family = AF_INET;
|
||||
listener_addr.sin_port = htons(cfg->listen_port);
|
||||
inet_aton(cfg->ipaddr, &(listener_addr.sin_addr));
|
||||
memset(listener_addr.sin_zero, '\0', sizeof(listener_addr.sin_zero));
|
||||
|
||||
if(bind(listen_fd, (struct sockaddr*)&listener_addr, sizeof
|
||||
listener_addr) == -1)
|
||||
{
|
||||
LOG(stdout, "bind() failed\n");
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
if(listen(listen_fd, PENDING_CONN) == -1)
|
||||
{
|
||||
LOG(stdout, "listen() failed\n");
|
||||
return -1;
|
||||
}
|
||||
sprintf(p, "HTTP server listening on port:%d\n", cfg->listen_port);
|
||||
LOG(stdout, p);
|
||||
while(1)
|
||||
{
|
||||
|
||||
new_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &addr_len);
|
||||
if(new_fd == -1)
|
||||
{
|
||||
//log
|
||||
LOG(stdout, "accept() failed\n");
|
||||
return -1;
|
||||
|
||||
}
|
||||
LOG(stdout, "new connection accepted\n");
|
||||
tdata = (int *)malloc(sizeof(int));
|
||||
*tdata = new_fd;
|
||||
rc = pthread_create(&thr, NULL, (void *)http_server,
|
||||
(void *)tdata);
|
||||
if (rc){
|
||||
printf("ERROR; return code from pthread_create() is %d\n", rc);
|
||||
exit(-1);
|
||||
}
|
||||
//fork a new process to handle this request
|
||||
#if 0
|
||||
if((pid = fork()) == -1)
|
||||
{
|
||||
//LOG Error
|
||||
LOG(stdout, "Error in fork\n");
|
||||
|
||||
}
|
||||
else if(pid == 0)
|
||||
{
|
||||
/*This is the child process. This will service the
|
||||
*request while parent goes back to listening.*/
|
||||
LOG(stdout, "Servicing request\n");
|
||||
http_server(cfg, new_fd);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
close(new_fd);
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
/*
|
||||
* File Name : tokenize.c
|
||||
* Author : Gaurav Tungatkar
|
||||
* Creation Date : 14-01-2011
|
||||
* Description :
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include "tokenize.h"
|
||||
/*
|
||||
* tokenize()
|
||||
* str = pointer to string that has to be tokenized
|
||||
* dest = destination where separate token will be stored
|
||||
*
|
||||
* This function separates tokens from the given string. Tokens can be of
|
||||
* maximum MAX_TOKEN_SIZE . After a call to tokenize, str points to the first
|
||||
* character after the current token; the string is consumed by the tokenize
|
||||
* routine.
|
||||
* RETURNS: length of current token
|
||||
* -1 if error
|
||||
*/
|
||||
int tokenize(char **str, char *dest)
|
||||
{
|
||||
int count = 0;
|
||||
while(isspace(**str))
|
||||
(*str)++;
|
||||
while(!isspace(**str) && (**str != '\0'))
|
||||
{
|
||||
*dest++ = *(*str)++;
|
||||
count++;
|
||||
if(count >= MAX_TOKEN_SIZE)
|
||||
{
|
||||
count = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
*dest = '\0';
|
||||
return count;
|
||||
}
|
||||
#define main nomain
|
||||
int main()
|
||||
{
|
||||
char *str = " Do I get all \n \n <tokens> ninechars ninetyninechars ";
|
||||
char dest[MAX_TOKEN_SIZE];
|
||||
int ret;
|
||||
while((ret = tokenize(&str, dest))> 0)
|
||||
{
|
||||
printf("token = %s\n", dest);
|
||||
}
|
||||
if(ret == -1)
|
||||
printf("Tokenization failed to complete\n");
|
||||
return 0;
|
||||
|
||||
}
|
@ -1,332 +0,0 @@
|
||||
/*
|
||||
* File Name : wcache.c
|
||||
* Author : Gaurav Tungatkar
|
||||
* Creation Date : 07-03-2011
|
||||
* Description :
|
||||
* Web cache
|
||||
* This includes generic linked list implementation as well as wrapper function
|
||||
* for managing cache.
|
||||
* Cache design :
|
||||
* Each cache object is linked in a global double linked list "cache"
|
||||
* It is also part of the list pointed to by a HashTable entry.
|
||||
* Each cache object contains multiple "fragments" - nothing but data buffers,
|
||||
* linked together.
|
||||
* Hash table stores pointers to cache based on hash value of http GET reqest
|
||||
* line.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <sys/time.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include "wcache.h"
|
||||
#include "httpconf.h"
|
||||
struct wcache_list * wcache_hashtable[WCACHE_HASHTABLE_SIZE];
|
||||
struct wcache_list cache_list;
|
||||
extern struct http_server_config cfg;
|
||||
extern pthread_mutex_t list_mutex;
|
||||
/* very rudimentary hash function */
|
||||
unsigned int hash(char * buf)
|
||||
{
|
||||
unsigned int hashv = 1298; //seed
|
||||
const char *s = buf;
|
||||
while (*s)
|
||||
{
|
||||
hashv = hashv * 101 + *s++;
|
||||
}
|
||||
return hashv;
|
||||
}
|
||||
|
||||
struct wcache_entry * wcache_entry_alloc()
|
||||
{
|
||||
struct wcache_entry *e = (struct wcache_entry *)malloc(sizeof(struct wcache_entry));
|
||||
memset(e, 0, sizeof(struct wcache_entry));
|
||||
pthread_mutex_init(&(e->entry_lock), NULL);
|
||||
return e;
|
||||
}
|
||||
int lock_entry(struct wcache_entry *w)
|
||||
{
|
||||
return pthread_mutex_lock(&(w->entry_lock));
|
||||
|
||||
}
|
||||
int unlock_entry(struct wcache_entry *w)
|
||||
{
|
||||
return pthread_mutex_unlock(&(w->entry_lock));
|
||||
|
||||
}
|
||||
void wcache_table_init()
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < WCACHE_HASHTABLE_SIZE; i++)
|
||||
{
|
||||
wcache_hashtable[i] = NULL;
|
||||
}
|
||||
}
|
||||
struct wcache_list * wcache_list_alloc()
|
||||
{
|
||||
struct wcache_list * l = (struct wcache_list*)malloc(sizeof(struct wcache_list));
|
||||
memset(l, 0, sizeof(struct wcache_list));
|
||||
return l;
|
||||
}
|
||||
void wcache_list_init(struct wcache_list *l)
|
||||
{
|
||||
assert(l != NULL);
|
||||
l->head.next = l->head.prev = &(l->head);
|
||||
}
|
||||
|
||||
|
||||
/*add element to a list*/
|
||||
int wcache_list_add(struct wcache_list *l, struct wcache_list_element *e)
|
||||
{
|
||||
if(e == NULL) goto ERR;
|
||||
if(l == NULL) goto ERR;
|
||||
struct wcache_list_element * last;
|
||||
if(l->head.prev == NULL)
|
||||
{
|
||||
l->head.prev = l->head.next = e;
|
||||
e->prev = &(l->head);
|
||||
e->next = &(l->head);
|
||||
return OK;
|
||||
}
|
||||
last = l->head.prev;
|
||||
last->next = e;
|
||||
e->prev = last;
|
||||
e->next = &(l->head);
|
||||
l->head.prev = e;
|
||||
return OK;
|
||||
ERR:
|
||||
//LOG
|
||||
return ERROR;
|
||||
|
||||
}
|
||||
|
||||
int wcache_list_del(struct wcache_list_element *e)
|
||||
{
|
||||
if(e == NULL) goto ERR;
|
||||
e->prev->next = e->next;
|
||||
e->next->prev = e->prev;
|
||||
e->prev = NULL;
|
||||
e->next = NULL;
|
||||
return OK;
|
||||
ERR:
|
||||
return ERROR;
|
||||
|
||||
}
|
||||
/*add a fragment to this entry*/
|
||||
int wcache_fragment_add(struct wcache_entry *entry, char *buffer, int size)
|
||||
{
|
||||
struct fragment_buffer *fb =
|
||||
(struct fragment_buffer *)malloc(sizeof(struct fragment_buffer));
|
||||
int ret;
|
||||
assert(fb);
|
||||
// memset(fb, 0, sizeof(struct fragment_buffer));
|
||||
memcpy(fb->buffer, buffer, FRAGMENT_SIZE);
|
||||
fb->size = size;
|
||||
entry->size += size + sizeof(struct fragment_buffer);
|
||||
pthread_mutex_lock(&list_mutex);
|
||||
ret = wcache_list_add(&(entry->fragments), &(fb->elem));
|
||||
pthread_mutex_unlock(&list_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int wcache_entry_replace(struct wcache *w, struct wcache_entry *entry)
|
||||
{
|
||||
//cache entry replacement
|
||||
//for now, modified FIFO policy followed- replace the oldest entry
|
||||
//whose deletion leaves __enough space__ in the cache for the current entry.
|
||||
//This means, that the "oldest" entry may not be the one that is always
|
||||
//replaced.
|
||||
struct wcache_list_element *e = &(w->l.head);
|
||||
|
||||
struct wcache_entry * we;
|
||||
if(e == NULL)
|
||||
return OK;
|
||||
|
||||
for(; e->next != &(w->l.head); e = e->next)
|
||||
{
|
||||
we = container_of(e->next, struct wcache_entry, cache_elem);
|
||||
if((w->curr_size + entry->size - we->size) <= w->max_size)
|
||||
{
|
||||
w->curr_size =- we->size;
|
||||
printf("Removing %s\n", we->http_signature);
|
||||
wcache_remove_entry(we);
|
||||
return OK;
|
||||
}
|
||||
|
||||
}
|
||||
return ERROR;
|
||||
|
||||
}
|
||||
/*add an entry to cache. Handle cache full and replacement*/
|
||||
int wcache_add(struct wcache *w, struct wcache_entry *entry)
|
||||
{
|
||||
struct wcache_list * l = &(w->l);
|
||||
struct wcache_list *hte;
|
||||
if(pthread_mutex_lock(&list_mutex) != 0)
|
||||
return ERROR;
|
||||
if(w->curr_size + entry->size >= w->max_size)
|
||||
{
|
||||
//cache full. remove one entry and make space.
|
||||
wcache_entry_replace(w, entry);
|
||||
|
||||
}
|
||||
if(w->curr_size + entry->size >= w->max_size)
|
||||
{
|
||||
//LOG
|
||||
goto ERR;
|
||||
|
||||
}
|
||||
|
||||
if(wcache_list_add(l, &(entry->cache_elem)) == ERROR)
|
||||
goto ERR;
|
||||
|
||||
w->curr_size += entry->size;
|
||||
hte = wcache_hashtable[entry->signature_hash % WCACHE_HASHTABLE_SIZE];
|
||||
if(hte == NULL)
|
||||
{
|
||||
hte = wcache_hashtable[entry->signature_hash % WCACHE_HASHTABLE_SIZE] =
|
||||
wcache_list_alloc();
|
||||
|
||||
}
|
||||
if(wcache_list_add(hte, &(entry->hash_elem)) == ERROR)
|
||||
goto ERR;
|
||||
|
||||
pthread_mutex_unlock(&list_mutex);
|
||||
return OK;
|
||||
ERR:
|
||||
pthread_mutex_unlock(&list_mutex);
|
||||
//LOG
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
/*find the cache entry corresponding to this http signature*/
|
||||
struct wcache_entry * wcache_find(char http_signature[HTTP_SIGNATURE_SIZE],
|
||||
time_t ts)
|
||||
{
|
||||
/*
|
||||
* ts - either the if modified since time or the current time
|
||||
* */
|
||||
/* TODO */
|
||||
unsigned int hashv = hash(http_signature);
|
||||
struct wcache_list *l = NULL;
|
||||
struct wcache_list_element *e;
|
||||
struct wcache_entry * we;
|
||||
|
||||
if(pthread_mutex_lock(&list_mutex) != 0)
|
||||
return NULL;
|
||||
l = wcache_hashtable[hashv % WCACHE_HASHTABLE_SIZE];
|
||||
if(l)
|
||||
e = &(l->head);
|
||||
else
|
||||
goto RET;
|
||||
for(;e->next != &(l->head); e = e->next)
|
||||
{
|
||||
//w = l;
|
||||
we = container_of((e->next), struct wcache_entry, hash_elem);
|
||||
if(!we->valid)
|
||||
goto RET;
|
||||
if(strcmp(http_signature, we->http_signature) == 0)
|
||||
{
|
||||
goto RETWE;
|
||||
}
|
||||
|
||||
}
|
||||
RET:
|
||||
pthread_mutex_unlock(&list_mutex);
|
||||
return NULL;
|
||||
RETWE:
|
||||
pthread_mutex_unlock(&list_mutex);
|
||||
return we;
|
||||
|
||||
}
|
||||
|
||||
/*free all fragments of this entry*/
|
||||
int wcache_remove_fragments(struct wcache_entry *w)
|
||||
{
|
||||
struct fragment_buffer *b;
|
||||
if(w->fragments.head.next)
|
||||
{
|
||||
while(w->fragments.head.next != w->fragments.head.prev)
|
||||
{
|
||||
b = container_of(w->fragments.head.next,
|
||||
struct fragment_buffer, elem);
|
||||
wcache_list_del(w->fragments.head.next);
|
||||
w->size -= b->size;
|
||||
if(b) free(b);
|
||||
}
|
||||
}
|
||||
return OK;
|
||||
|
||||
}
|
||||
int wcache_remove_entry(struct wcache_entry *w)
|
||||
{
|
||||
|
||||
if(wcache_list_del(&(w->cache_elem)) == ERROR)
|
||||
{
|
||||
printf(":2 check\n");
|
||||
return ERROR;
|
||||
}
|
||||
if(wcache_list_del(&(w->hash_elem)) == ERROR)
|
||||
{
|
||||
printf(":3 check\n");
|
||||
return ERROR;
|
||||
}
|
||||
printf(":4 check\n");
|
||||
//free every fragment
|
||||
wcache_remove_fragments(w);
|
||||
pthread_mutex_destroy(&(w->entry_lock));
|
||||
free(w);
|
||||
w = NULL;
|
||||
|
||||
return OK;
|
||||
}
|
||||
#if 0
|
||||
//void * alloc_wcache_hashtable();
|
||||
int main11()
|
||||
{
|
||||
struct wcache_entry *entry =
|
||||
(struct wcache_entry *)malloc(sizeof(struct wcache_entry));
|
||||
// cache_list.head = (struct wcache_list_element*)malloc(
|
||||
// sizeof (struct wcache_list_element));
|
||||
/* wcache_list_add(&cache_list, &(entry->cache_elem));
|
||||
entry = (struct wcache_entry *)malloc(sizeof(struct wcache_entry));
|
||||
entry->signature_hash = 30;
|
||||
wcache_list_add(&cache_list, &(entry->cache_elem));
|
||||
entry = NULL;
|
||||
entry = get_struct(cache_list.head.next, struct wcache_entry,
|
||||
cache_elem);
|
||||
printf("TC1: %d\n", entry->signature_hash);
|
||||
entry = get_struct(cache_list.head.next->next, struct wcache_entry,
|
||||
cache_elem);
|
||||
printf("TC1: %d\n", entry->signature_hash);
|
||||
*/
|
||||
cache.curr_size= 0;
|
||||
cache.max_size = 5;
|
||||
// cache.l.head.next = cache.l.head.prev = &(cache.l.head);
|
||||
wcache_list_init(&(cache.l));
|
||||
entry->size = 15;
|
||||
strcpy(entry->http_signature, "HTTP/1.10");
|
||||
entry->signature_hash = hash(entry->http_signature);
|
||||
entry->valid = 1;
|
||||
entry->ts = 10;
|
||||
wcache_add(&cache, entry);
|
||||
entry = (struct wcache_entry *)malloc(sizeof(struct wcache_entry));
|
||||
entry->size = 10;
|
||||
strcpy(entry->http_signature, "HTTP/2.22");
|
||||
entry->signature_hash = hash(entry->http_signature);
|
||||
entry->valid = 1;
|
||||
entry->ts = 10;
|
||||
wcache_add(&cache, entry);
|
||||
entry = NULL;
|
||||
entry = wcache_find("HTTP/1.10", 12);
|
||||
if(entry != NULL)
|
||||
printf("TC1: %s\n", entry->http_signature);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
@ -1,16 +0,0 @@
|
||||
SOURCES := $(shell find . -iname '*.c' -depth 1 )
|
||||
OBJECTS := $(SOURCES:.c=.o)
|
||||
|
||||
all: http_proxy tests
|
||||
|
||||
http_proxy: $(OBJECTS)
|
||||
gcc $(OBJECTS) $(LFLAGS) -o http_proxy
|
||||
|
||||
.PHONY: tests
|
||||
|
||||
tests:
|
||||
gcc -I.. tests/http_message_test.c http_message.c -o http_message_test
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm $(OBJECTS) http_proxy
|
@ -1,6 +0,0 @@
|
||||
HTTPProxy
|
||||
|
||||
Description:
|
||||
HTTPProxy is a basic proxy server for http.
|
||||
|
||||
|
@ -1,33 +0,0 @@
|
||||
User Manual - NetNinny
|
||||
======================
|
||||
|
||||
### Building
|
||||
|
||||
To build the software, simply unpack the archive and issue “make” in the HTTPProxy directory. This will produce a NetNinny binary in the same catalogue.
|
||||
|
||||
### Configuration
|
||||
|
||||
It is possible to configure yourself what port the proxy server should use. This can be done by giving the port number as a argument to the executable. For example:
|
||||
|
||||
./NetNinny 8081
|
||||
|
||||
### Features
|
||||
|
||||
NetNinny has the following features:
|
||||
|
||||
* Support for both HTTP version 1.0 and 1.1
|
||||
* Can handle any size of data received from the web server
|
||||
* Blocks URLs containing forbidden words
|
||||
* Blocks content containing forbidden words
|
||||
* Compatible with all major browsers, i.e follows the specification
|
||||
|
||||
### Lacks
|
||||
|
||||
We have not implemented any other request than GET currently. It also lacks complete error checking, so currently it is not hard, probably, to get the server to crash with a bad request.
|
||||
|
||||
### Proof
|
||||
|
||||
We have tested the proxy server with sites like Aftonbladet, Expressen, Wikipedia, Facebook and YouTube and it seems to work fine.
|
||||
|
||||
|
||||
|
Binary file not shown.
Binary file not shown.
@ -1,227 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/queue.h>
|
||||
#include "proxy.h"
|
||||
|
||||
|
||||
int http_methods_len = 9;
|
||||
const char *http_methods[] =
|
||||
{
|
||||
"OPTIONS",
|
||||
"GET",
|
||||
"HEAD",
|
||||
"POST",
|
||||
"PUT",
|
||||
"DELETE",
|
||||
"TRACE",
|
||||
"CONNECT",
|
||||
"INVALID"
|
||||
};
|
||||
|
||||
void http_request_init(http_request **req)
|
||||
{
|
||||
*req = (http_request*)malloc(sizeof(http_request));
|
||||
|
||||
http_request *request = *req;
|
||||
request->method = 0;
|
||||
request->search_path = NULL;
|
||||
|
||||
TAILQ_INIT(&request->metadata_head);
|
||||
}
|
||||
|
||||
void http_request_destroy(http_request *req)
|
||||
{
|
||||
free((char*)req->search_path);
|
||||
|
||||
struct http_metadata_item *item;
|
||||
TAILQ_FOREACH(item, &req->metadata_head, entries) {
|
||||
free((char*)item->key);
|
||||
free((char*)item->value);
|
||||
free(item);
|
||||
}
|
||||
}
|
||||
|
||||
void http_request_print(http_request *req)
|
||||
{
|
||||
printf("[HTTP_REQUEST] \n");
|
||||
|
||||
switch (req->version) {
|
||||
case HTTP_VERSION_1_0:
|
||||
printf("version:\tHTTP/1.0\n");
|
||||
break;
|
||||
case HTTP_VERSION_1_1:
|
||||
printf("version:\tHTTP/1.1\n");
|
||||
break;
|
||||
case HTTP_VERSION_INVALID:
|
||||
printf("version:\tInvalid\n");
|
||||
break;
|
||||
}
|
||||
|
||||
printf("method:\t\t%s\n",
|
||||
http_methods[req->method]);
|
||||
printf("path:\t\t%s\n",
|
||||
req->search_path);
|
||||
|
||||
printf("[Metadata] \n");
|
||||
struct http_metadata_item *item;
|
||||
TAILQ_FOREACH(item, &req->metadata_head, entries) {
|
||||
printf("%s: %s\n", item->key, item->value);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void http_parse_method(http_request* result, const char* line)
|
||||
{
|
||||
enum parser_states {
|
||||
METHOD,
|
||||
URL,
|
||||
VERSION,
|
||||
DONE
|
||||
};
|
||||
|
||||
char* copy;
|
||||
char* p;
|
||||
copy = p = strdup(line);
|
||||
char* token = NULL;
|
||||
int s = METHOD;
|
||||
|
||||
while ((token = strsep(&p, " \r\n")) != NULL) {
|
||||
switch (s) {
|
||||
case METHOD: {
|
||||
int found = 0;
|
||||
int i;
|
||||
for (i = 0; i < http_methods_len; i++) {
|
||||
if (strcmp(token, http_methods[i]) == 0) {
|
||||
found = 1;
|
||||
result->method = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found == 0) {
|
||||
result->method = http_methods_len - 1;
|
||||
free(copy);
|
||||
return;
|
||||
}
|
||||
s++;
|
||||
break;
|
||||
}
|
||||
case URL:
|
||||
result->search_path = strdup(token);
|
||||
s++;
|
||||
break;
|
||||
case VERSION:
|
||||
{
|
||||
if(strcmp(token, "HTTP/1.0") == 0) {
|
||||
result->version = HTTP_VERSION_1_0;
|
||||
} else if(strcmp(token, "HTTP/1.1") == 0) {
|
||||
result->version = HTTP_VERSION_1_1;
|
||||
} else {
|
||||
result->version = HTTP_VERSION_INVALID;
|
||||
}
|
||||
s++;
|
||||
break;
|
||||
}
|
||||
case DONE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(copy);
|
||||
return;
|
||||
}
|
||||
|
||||
// Content-Byte: 101
|
||||
void http_parse_metadata(http_request *result, char *line)
|
||||
{
|
||||
char *line_copy = strdup(line);
|
||||
char *key = strdup(strtok(line_copy, ":"));
|
||||
|
||||
char *value = strtok(NULL, "\r");
|
||||
|
||||
// remove whitespaces :)
|
||||
char *p = value;
|
||||
while(*p == ' ') p++;
|
||||
value = strdup(p);
|
||||
|
||||
free(line_copy);
|
||||
|
||||
// create the http_metadata_item object and
|
||||
// put the data in it
|
||||
http_metadata_item *item = malloc(sizeof(*item));
|
||||
item->key = key;
|
||||
item->value = value;
|
||||
|
||||
// add the new item to the list of metadatas
|
||||
TAILQ_INSERT_TAIL(&result->metadata_head, item, entries);
|
||||
}
|
||||
|
||||
|
||||
|
||||
char *http_build_request(http_request *req)
|
||||
{
|
||||
const char *search_path = req->search_path;
|
||||
|
||||
// construct the http request
|
||||
int size = strlen("GET ") + 1;
|
||||
//char *request_buffer = calloc(sizeof(char)*size);
|
||||
char *request_buffer = calloc(size, sizeof(char));
|
||||
strncat(request_buffer, "GET ", 4);
|
||||
|
||||
size += strlen(search_path) + 1;
|
||||
request_buffer = realloc(request_buffer, size);
|
||||
strncat(request_buffer, search_path, strlen(search_path));
|
||||
|
||||
// TODO: Check the actual HTTP version that is used, and if
|
||||
// 1.1 is used we should append:
|
||||
// Connection: close
|
||||
// to the header.
|
||||
switch(req->version)
|
||||
{
|
||||
case HTTP_VERSION_1_0:
|
||||
size += strlen(" HTTP/1.0\r\n\r\n");
|
||||
request_buffer = realloc(request_buffer, size);
|
||||
strncat(request_buffer, " HTTP/1.0\r\n", strlen(" HTTP/1.0\r\n"));
|
||||
break;
|
||||
case HTTP_VERSION_1_1:
|
||||
size += strlen(" HTTP/1.1\r\n\r\n");
|
||||
request_buffer = realloc(request_buffer, size);
|
||||
strncat(request_buffer, " HTTP/1.1\r\n", strlen(" HTTP/1.1\r\n"));
|
||||
break;
|
||||
default:
|
||||
LOG(LOG_ERROR, "Failed to retrieve the http version\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
http_metadata_item *item;
|
||||
TAILQ_FOREACH(item, &req->metadata_head, entries) {
|
||||
// Remove Connection properties in header in case
|
||||
// there are any
|
||||
if(strcmp(item->key, "Connection") == 0 ||
|
||||
strcmp(item->key, "Proxy-Connection") == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
size += strlen(item->key) + strlen(": ") + strlen(item->value) + strlen("\r\n");
|
||||
request_buffer = realloc(request_buffer, size);
|
||||
strncat(request_buffer, item->key, strlen(item->key));
|
||||
strncat(request_buffer, ": ", 2);
|
||||
strncat(request_buffer, item->value, strlen(item->value));
|
||||
strncat(request_buffer, "\r\n", 2);
|
||||
}
|
||||
|
||||
if(req->version == HTTP_VERSION_1_1)
|
||||
{
|
||||
size += strlen("Connection: close\r\n");
|
||||
request_buffer = realloc(request_buffer, size);
|
||||
strncat(request_buffer, "Connection: close\r\n", strlen("Connection: close\r\n"));
|
||||
}
|
||||
|
||||
|
||||
size += strlen("\r\n");
|
||||
request_buffer = realloc(request_buffer, size);
|
||||
strncat(request_buffer, "\r\n", 2);
|
||||
|
||||
return request_buffer;
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
#ifndef HTTP_MESSAGE_HH
|
||||
#define HTTP_MESSAGE_HH
|
||||
#include "proxy.h"
|
||||
|
||||
void http_request_init(http_request**);
|
||||
void http_request_destroy(http_request*);
|
||||
void http_request_print(http_request*);
|
||||
void http_parse_method(http_request*, char*);
|
||||
void http_parse_metadata(http_request*, char*);
|
||||
char *http_build_request(http_request*);
|
||||
|
||||
extern int http_methods_len;
|
||||
extern const char* http_methods[];
|
||||
|
||||
#endif
|
@ -1,28 +0,0 @@
|
||||
#include <sys/queue.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "list.h"
|
||||
#include "proxy.h"
|
||||
|
||||
const char *list_get_key(struct METADATA_HEAD *list, const char *key)
|
||||
{
|
||||
http_metadata_item *item;
|
||||
TAILQ_FOREACH(item, list, entries) {
|
||||
if(strcmp(item->key, key) == 0)
|
||||
{
|
||||
return item->value;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void list_add_key(struct METADATA_HEAD *list, const char *key, const char *value)
|
||||
{
|
||||
http_metadata_item *item = (http_metadata_item*)malloc(sizeof(http_metadata_item));
|
||||
item->key = key;
|
||||
item->value = value;
|
||||
|
||||
TAILQ_INSERT_TAIL(list, item, entries);
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
#ifndef LIST_HH
|
||||
#define LIST_HH
|
||||
|
||||
#include "proxy.h"
|
||||
|
||||
const char *list_get_key(struct METADATA_HEAD *list, const char *key);
|
||||
|
||||
#endif
|
@ -1,313 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
#include <sys/queue.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "proxy.h"
|
||||
#include "net.h"
|
||||
#include "list.h"
|
||||
#include "http_message.h"
|
||||
|
||||
char *read_line(int sockfd)
|
||||
{
|
||||
int buffer_size = 2;
|
||||
char *line = (char*)malloc(sizeof(char)*buffer_size+1);
|
||||
char c;
|
||||
int length = 0;
|
||||
int counter = 0;
|
||||
|
||||
while(1)
|
||||
{
|
||||
length = recv(sockfd, &c, 1, 0);
|
||||
line[counter++] = c;
|
||||
|
||||
if(c == '\n')
|
||||
{
|
||||
line[counter] = '\0';
|
||||
return line;
|
||||
}
|
||||
|
||||
// reallocate the buffer
|
||||
if(counter == buffer_size)
|
||||
{
|
||||
buffer_size *= 2;
|
||||
|
||||
// TODO: should probably allocate +1 for the null terminator,
|
||||
// but not sure.
|
||||
line = (char*)realloc(line, sizeof(char)*buffer_size);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int containing_forbidden_words(char str[]){
|
||||
|
||||
// Forbidden words
|
||||
char *words[] = {"SpongeBob", "Britney Spears", "Paris Hilton", "Norrkӧping", "Norrköping", "Norrk%C3%B6ping"};
|
||||
int hits[] = {0, 0, 0, 0, 0, 0}; // Every forbidden word need to have a zero in this array to be able to count number of char hits.
|
||||
int numb_words = 6; // Number of forbidden words
|
||||
|
||||
int str_length = strlen(str);
|
||||
int c, w; // Index for char in str, and index for word in words
|
||||
|
||||
// Search for forbidden words
|
||||
for (c = 0; c < str_length; c++)
|
||||
{
|
||||
for (w = 0; w < numb_words; w++)
|
||||
{
|
||||
if (tolower(words[w][ hits[w] ]) == tolower(str[c])){
|
||||
if(++hits[w] == strlen(words[w]))
|
||||
return 1;
|
||||
}
|
||||
else if (hits[w] != 0)
|
||||
hits[w--] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int send_to_client(int client_sockfd, char data[], int packages_size, ssize_t length)
|
||||
{
|
||||
// if packages_size is set to 0, then the function will try to send all data as one package.
|
||||
if(packages_size < 1)
|
||||
{
|
||||
if(send(client_sockfd, data, length, 0) == -1)
|
||||
{
|
||||
perror("Couldn't send data to the client.");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int p;
|
||||
for(p = 0; p*packages_size + packages_size < length; p++){
|
||||
if(send(client_sockfd, (data + p*packages_size), packages_size, 0) == -1)
|
||||
{
|
||||
perror("Couldn't send any or just some data to the client. (loop)\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (p*packages_size < length)
|
||||
{
|
||||
if(send(client_sockfd, (data + p*packages_size), length - p*packages_size, 0) == -1)
|
||||
{
|
||||
perror("Couldn't send any or just some data to the client.\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int http_request_send(int sockfd, http_request *req)
|
||||
{
|
||||
LOG(LOG_TRACE, "Requesting: %s\n", req->search_path);
|
||||
|
||||
char *request_buffer = http_build_request(req);
|
||||
|
||||
// send the http request to the web server
|
||||
if(send(sockfd, request_buffer, strlen(request_buffer), 0) == -1)
|
||||
{
|
||||
free(request_buffer);
|
||||
perror("send");
|
||||
return 1;
|
||||
}
|
||||
free(request_buffer);
|
||||
|
||||
LOG(LOG_TRACE, "Sent HTTP header to web server\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void handle_client(int client_sockfd)
|
||||
{
|
||||
char *line;
|
||||
int server_sockfd;
|
||||
http_request *req;
|
||||
|
||||
req = http_read_header(client_sockfd);
|
||||
if(req == NULL)
|
||||
{
|
||||
LOG(LOG_ERROR, "Failed to parse the header\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (containing_forbidden_words((char*)req->search_path) || containing_forbidden_words((char*)list_get_key(&req->metadata_head, "Host"))){
|
||||
char *error1 = "HTTP/1.1 200 OK\r\nServer: Net Ninny\r\nContent-Type: text/html\r\n\r\n<html>\n\n<title>\nNet Ninny Error Page 1 for CPSC 441 Assignment 1\n</title>\n\n<body>\n<p>\nSorry, but the Web page that you were trying to access\nis inappropriate for you, based on the URL.\nThe page has been blocked to avoid insulting your intelligence.\n</p>\n\n<p>\nNet Ninny\n</p>\n\n</body>\n\n</html>\n";
|
||||
http_request_destroy(req);
|
||||
send_to_client(client_sockfd, error1, 0, strlen(error1));
|
||||
return;
|
||||
}
|
||||
|
||||
server_sockfd = http_connect(req);
|
||||
if(server_sockfd == -1)
|
||||
{
|
||||
LOG(LOG_ERROR, "Failed to connect to host\n");
|
||||
http_request_destroy(req);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(LOG_TRACE, "Connected to host\n");
|
||||
|
||||
http_request_send(server_sockfd, req);
|
||||
http_request_destroy(req);
|
||||
|
||||
LOG(LOG_TRACE, "Beginning to retrieve the response header\n");
|
||||
int is_bad_encoding = 0;
|
||||
int is_text_content = 0;
|
||||
int line_length;
|
||||
while(1)
|
||||
{
|
||||
line = read_line(server_sockfd);
|
||||
line_length = strlen(line);
|
||||
send_to_client(client_sockfd, line, 0, line_length);
|
||||
|
||||
if(line[0] == '\r' && line[1] == '\n')
|
||||
{
|
||||
// We received the end of the HTTP header
|
||||
LOG(LOG_TRACE, "Received the end of the HTTP response header\n");
|
||||
free(line);
|
||||
break;
|
||||
}
|
||||
else if(18 <= line_length)
|
||||
{
|
||||
line[18] = '\0'; // Destroys the data in the line, but is needed to check if in coming data will be text format.
|
||||
if (strcmp(line, "Content-Type: text") == 0)
|
||||
is_text_content = 1;
|
||||
else if (strcmp(line, "Content-Encoding: ") == 0)
|
||||
is_bad_encoding = 1;
|
||||
}
|
||||
|
||||
free(line);
|
||||
}
|
||||
|
||||
LOG(LOG_TRACE, "Beginning to retrieve content\n");
|
||||
ssize_t chunk_length;
|
||||
char *temp = http_read_chunk(server_sockfd, &chunk_length);
|
||||
LOG(LOG_TRACE, "Received the content, %d bytes\n", (int)chunk_length);
|
||||
|
||||
if (is_text_content && !is_bad_encoding && containing_forbidden_words(temp))
|
||||
{
|
||||
LOG(LOG_TRACE, "Received data contains forbidden words!\n");
|
||||
char *error2 = "<html>\n<title>\nNet Ninny Error Page 3 for CPSC 441 Assignment 1\n</title>\n\n<body>\n<p>\nSorry, but the Web page that you were trying to access\nis inappropriate for you, based on some of the words it contains.\nThe page has been blocked to avoid insulting your intelligence.\n</p>\n\n<p>\nNet Ninny\n</p>\n\n</body>\n\n</html>\n";
|
||||
|
||||
send_to_client(client_sockfd, error2, 0, strlen(error2));
|
||||
}
|
||||
else
|
||||
send_to_client(client_sockfd, temp, 0, chunk_length);
|
||||
free(temp);
|
||||
close(server_sockfd);
|
||||
}
|
||||
|
||||
void start_server(char *port)
|
||||
{
|
||||
printf("Starting server\n");
|
||||
|
||||
int sockfd, new_fd;
|
||||
struct addrinfo hints, *servinfo, *p;
|
||||
struct sockaddr_storage their_addr;
|
||||
socklen_t sin_size;
|
||||
int rv;
|
||||
int yes = 1;
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
|
||||
if((rv = getaddrinfo(NULL, port, &hints, &servinfo)) != 0)
|
||||
{
|
||||
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
|
||||
return;
|
||||
}
|
||||
|
||||
for(p = servinfo; p != NULL; p = p->ai_next)
|
||||
{
|
||||
if((sockfd = socket(p->ai_family, p->ai_socktype,
|
||||
p->ai_protocol)) == -1)
|
||||
{
|
||||
perror("server: socket");
|
||||
continue;
|
||||
}
|
||||
|
||||
if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
|
||||
sizeof(int)) == -1)
|
||||
{
|
||||
perror("setsockopt");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if(bind(sockfd, p->ai_addr, p->ai_addrlen) == -1)
|
||||
{
|
||||
close(sockfd);
|
||||
perror("server: bind");
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if(p == NULL)
|
||||
{
|
||||
fprintf(stderr, "server: failed to bind\n");
|
||||
return;
|
||||
}
|
||||
|
||||
freeaddrinfo(servinfo);
|
||||
|
||||
if(listen(sockfd, 10) == -1)
|
||||
{
|
||||
perror("listen");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf("server: waiting for connections..\n");
|
||||
while(1)
|
||||
{
|
||||
sin_size = sizeof(their_addr);
|
||||
new_fd = accept(sockfd, (struct sockaddr*)&their_addr, &sin_size);
|
||||
if(new_fd == -1)
|
||||
{
|
||||
perror("accept");
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("Receieved connection\n");
|
||||
|
||||
signal(SIGCHLD, SIG_IGN);
|
||||
pid_t child_pid = fork();
|
||||
if(!child_pid)
|
||||
{
|
||||
handle_client(new_fd);
|
||||
|
||||
close(new_fd);
|
||||
exit(0);
|
||||
}
|
||||
close(new_fd);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char *port = "8080";
|
||||
if (argc > 1)
|
||||
port = argv[1];
|
||||
start_server(port);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,194 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
#include <sys/queue.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "proxy.h"
|
||||
#include "list.h"
|
||||
#include "http_message.h"
|
||||
|
||||
/*
|
||||
Creates a TCP connection to the given host
|
||||
and returns the socket. Returns -1 if something
|
||||
fails.
|
||||
*/
|
||||
int http_connect(http_request *req)
|
||||
{
|
||||
char *host = (char*)list_get_key(&req->metadata_head, "Host");
|
||||
char *port = strstr(host, ":");
|
||||
|
||||
if(port == NULL)
|
||||
{
|
||||
// set port to default
|
||||
port = calloc(3, sizeof(char));
|
||||
strncat(port, "80", 2);
|
||||
|
||||
LOG(LOG_TRACE, "Using default port\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
// remove the port number from the host
|
||||
host = strtok(host, ":");
|
||||
|
||||
// jump over the ':' char
|
||||
port++;
|
||||
|
||||
LOG(LOG_TRACE, "Using port: %s\n", port);
|
||||
}
|
||||
|
||||
|
||||
LOG(LOG_TRACE, "Connecting to HTTP server: %s\n", host);
|
||||
|
||||
if(host == NULL)
|
||||
{
|
||||
LOG(LOG_ERROR, "Could not find the Host property in the metadata\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct addrinfo hints, *servinfo, *p;
|
||||
int sockfd, rv;
|
||||
|
||||
memset(&hints, 0, sizeof hints);
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
if((rv = getaddrinfo(host, port, &hints, &servinfo)) != 0)
|
||||
{
|
||||
LOG(LOG_ERROR, "Failed to lookup hostname\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// loop through all the results and connect to the first we can
|
||||
for(p = servinfo; p != NULL; p = p->ai_next) {
|
||||
if ((sockfd = socket(p->ai_family, p->ai_socktype,
|
||||
p->ai_protocol)) == -1) {
|
||||
perror("client: socket");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
|
||||
close(sockfd);
|
||||
perror("client: connect");
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (p == NULL) {
|
||||
LOG(LOG_ERROR, "Failed to connect to HTTP server\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
/*
|
||||
Read a HTTP header from the given socket and
|
||||
returns a http_request*.
|
||||
*/
|
||||
http_request *http_read_header(int sockfd)
|
||||
{
|
||||
LOG(LOG_TRACE, "Reading header\n");
|
||||
http_request *req;
|
||||
http_request_init(&req);
|
||||
|
||||
char *line;
|
||||
line = read_line(sockfd);
|
||||
http_parse_method(req, line);
|
||||
|
||||
while(1)
|
||||
{
|
||||
line = read_line(sockfd);
|
||||
if(line[0] == '\r' && line[1] == '\n')
|
||||
{
|
||||
// We received the end of the HTTP header
|
||||
LOG(LOG_TRACE, "Received header\n");
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
http_parse_metadata(req, line);
|
||||
|
||||
free(line);
|
||||
}
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
/*
|
||||
Read as much data as possible from the given socket
|
||||
and returns it as a null terminated char pointer. Data
|
||||
returned from this function must be freed somewhere else.
|
||||
*/
|
||||
char *http_read_chunk(int sockfd, ssize_t *length)
|
||||
{
|
||||
if(length == NULL)
|
||||
{
|
||||
LOG(LOG_ERROR, "The length pointer supplied to http_read_chunk is NULL\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(sockfd == -1)
|
||||
{
|
||||
LOG(LOG_ERROR, "The socket given to http_read_chunk is invalid\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *buf = malloc(sizeof(char));
|
||||
memset(buf, '\0', sizeof(char));
|
||||
char c;
|
||||
int current_size = 1;
|
||||
|
||||
time_t timeout = 5;
|
||||
time_t start = time(NULL);
|
||||
|
||||
ssize_t total_bytes = 0;
|
||||
ssize_t num_bytes = 0;
|
||||
|
||||
while(1)
|
||||
{
|
||||
// check if we should timeout
|
||||
if(time(NULL) - start > timeout)
|
||||
{
|
||||
LOG(LOG_WARNING, "Request timed out\n");
|
||||
break;
|
||||
}
|
||||
|
||||
num_bytes = recv(sockfd, &c, 1, 0);
|
||||
|
||||
if(num_bytes <= -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if(num_bytes == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// reallocate the buffer so the new data will fit
|
||||
buf = realloc(buf, sizeof(char)*++current_size);
|
||||
buf[total_bytes] = c;
|
||||
|
||||
total_bytes += num_bytes;
|
||||
}
|
||||
|
||||
LOG(LOG_TRACE, "Received: %d\n", (int)total_bytes);
|
||||
|
||||
*length = total_bytes;
|
||||
|
||||
return buf;
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
#ifndef NET_HH
|
||||
#define NET_HH
|
||||
|
||||
int http_connect(http_request *req);
|
||||
http_request *http_read_header(int sockfd);
|
||||
char *http_read_chunk(int sockfd, ssize_t *length);
|
||||
|
||||
#endif
|
@ -1,59 +0,0 @@
|
||||
#ifndef PROXY_HH
|
||||
#define PROXY_HH
|
||||
|
||||
#include <sys/queue.h>
|
||||
|
||||
#define LOG_ERROR 0
|
||||
#define LOG_WARNING 1
|
||||
#define LOG_NOTICE 2
|
||||
#define LOG_TRACE 3
|
||||
|
||||
#define ACTIVE_LEVEL 3
|
||||
#define LOG(LEVEL, MSG, ...) \
|
||||
if(LEVEL <= ACTIVE_LEVEL) { \
|
||||
printf("LOG(%d): ", LEVEL); \
|
||||
printf(MSG, ##__VA_ARGS__); \
|
||||
} \
|
||||
|
||||
extern int http_methods_len;
|
||||
extern const char *http_methods[];
|
||||
|
||||
char *read_line(int sockfd);
|
||||
|
||||
enum http_methods_enum {
|
||||
OPTIONS,
|
||||
GET,
|
||||
HEAD,
|
||||
POST,
|
||||
PUT,
|
||||
DELETE,
|
||||
TRACE,
|
||||
CONNECT,
|
||||
UNKNOWN
|
||||
};
|
||||
|
||||
enum http_versions_enum {
|
||||
HTTP_VERSION_1_0,
|
||||
HTTP_VERSION_1_1,
|
||||
HTTP_VERSION_INVALID
|
||||
};
|
||||
|
||||
typedef struct http_request
|
||||
{
|
||||
enum http_methods_enum method;
|
||||
enum http_versions_enum version;
|
||||
const char *search_path;
|
||||
|
||||
TAILQ_HEAD(METADATA_HEAD, http_metadata_item) metadata_head;
|
||||
} http_request;
|
||||
|
||||
typedef struct http_metadata_item
|
||||
{
|
||||
const char *key;
|
||||
const char *value;
|
||||
|
||||
TAILQ_ENTRY(http_metadata_item) entries;
|
||||
} http_metadata_item;
|
||||
|
||||
|
||||
#endif
|
@ -1,50 +0,0 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "../http_message.h"
|
||||
|
||||
int test_parse_valid_method()
|
||||
{
|
||||
printf("test_parse_valid_method - running\n");
|
||||
http_request* req;
|
||||
http_request_init(&req);
|
||||
char* data = "GET http://www.redd.it/hej/ HTTP/1.1\r\n";
|
||||
http_parse_method(req, data);
|
||||
assert(strcmp(http_methods[req->method], "GET") == 0);
|
||||
assert(req->version == HTTP_VERSION_1_1);
|
||||
assert(strcmp(req->search_path, "http://www.redd.it/hej/") == 0);
|
||||
http_request_destroy(req);
|
||||
printf("test_parse_valid_method - ok\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_parse_invalid_method()
|
||||
{
|
||||
printf("test_parse_invalid_method - running\n");
|
||||
http_request* req;
|
||||
http_request_init(&req);
|
||||
char* data = "FAKE http://www.redd.it/hej/ HTTP/1.1\r\n";
|
||||
http_parse_method(req, data);
|
||||
assert(strcmp(http_methods[req->method], "INVALID") == 0);
|
||||
printf("test_parse_invalid_method - ok\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_parse_invalid_http_version()
|
||||
{
|
||||
printf("test_parse_invalid_http_version - running\n");
|
||||
http_request* req;
|
||||
http_request_init(&req);
|
||||
char* data = "GET http://www.redd.it/hej/ HTTP/2.0\r\n";
|
||||
http_parse_method(req, data);
|
||||
assert(req->version == HTTP_VERSION_INVALID);
|
||||
printf("test_parse_invalid_http_version - ok\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_parse_valid_method();
|
||||
test_parse_invalid_method();
|
||||
test_parse_invalid_http_version();
|
||||
}
|
@ -1,249 +0,0 @@
|
||||
/************************************************
|
||||
*
|
||||
* file : DownloadTask.h
|
||||
* author: bobding
|
||||
* date : 2014-09-25
|
||||
* detail:
|
||||
*
|
||||
************************************************/
|
||||
|
||||
#ifndef _DOWNLOADTASK_H_
|
||||
#define _DOWNLOADTASK_H_
|
||||
|
||||
#include "TcpSocket.h"
|
||||
#include "HttpUtils.h"
|
||||
|
||||
#define MAXBUFFERSIZE 2048
|
||||
|
||||
//TcpSocket* socket;
|
||||
/*
|
||||
void initDownloadTask()
|
||||
{
|
||||
//socket = 0;
|
||||
initTcpSocket();
|
||||
//socket = new TcpSocket();
|
||||
}
|
||||
|
||||
void destroyDownloadTask()
|
||||
{
|
||||
if (0 != socket)
|
||||
{
|
||||
free(socket);
|
||||
}
|
||||
|
||||
socket = 0;
|
||||
}
|
||||
*/
|
||||
|
||||
int taskStart(const char* url, char** buffer, unsigned int* length)
|
||||
{
|
||||
/*if (0 == socket)
|
||||
{
|
||||
LogError("[DownloadTask::Start] invalid socket, url: %s.\n", url);
|
||||
return -1;
|
||||
}*/
|
||||
|
||||
int ret = socketOpen();
|
||||
if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct HttpUrl httpUrl = ParseUrl(url);
|
||||
|
||||
ret = socketConnect(httpUrl.host, httpUrl.port);
|
||||
if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = socketSend(httpUrl.request, strlen(httpUrl.request));
|
||||
if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
char dummy[MAXBUFFERSIZE];
|
||||
ret = socketRecv(dummy, MAXBUFFERSIZE); // receive 1st package.
|
||||
if (ret <= 0)
|
||||
{
|
||||
LogError("[DownloadTask::Start] recv head 0 bytes, url: %s.\n", url);
|
||||
return -1;
|
||||
}
|
||||
|
||||
dummy[ret] = '\0';
|
||||
|
||||
struct HttpHead httpHead = ParseHead(dummy, strlen(dummy));
|
||||
if (HS_FAIL == httpHead.httpState)
|
||||
{
|
||||
LogError("[DownloadTask::Start] recv failed, url: %s.\n", url);
|
||||
return -1;
|
||||
}
|
||||
else if (HS_RETRY == httpHead.httpState)
|
||||
{
|
||||
LogWarn("[DownloadTask::Start] retry, url: %s.\n", url);
|
||||
return taskStart(url, buffer, length);
|
||||
}
|
||||
else if (HS_REDIR == httpHead.httpState)
|
||||
{
|
||||
LogWarn("[DownloadTask::Start] redir, url: %s, location: %s.\n", url, httpHead.location);
|
||||
return taskStart(httpHead.location, buffer, length);
|
||||
}
|
||||
|
||||
*length = httpHead.contentLength;
|
||||
*buffer = (char *)malloc(*length * sizeof(char*));
|
||||
//*buffer = new char[*length];
|
||||
memset(*buffer, 0, *length);
|
||||
unsigned int offset = 0;
|
||||
|
||||
if (ret > httpHead.headLength)
|
||||
{
|
||||
memcpy((*buffer) + offset, dummy + httpHead.headLength, ret - httpHead.headLength);
|
||||
offset += ret - httpHead.headLength;
|
||||
}
|
||||
|
||||
while ((ret = socketRecv(dummy, MAXBUFFERSIZE)) >= 0)
|
||||
{
|
||||
LogPrompt("[DownloadTask::Start] received: %d, %d / %d bytes.\n", ret, offset, *length);
|
||||
|
||||
if (offset >= *length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (0 == ret)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (offset + ret > *length)
|
||||
{
|
||||
ret = *length - offset;
|
||||
}
|
||||
|
||||
memcpy((*buffer) + offset, dummy, ret);
|
||||
|
||||
offset += ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
class DownloadTask
|
||||
{
|
||||
public:
|
||||
DownloadTask()
|
||||
{
|
||||
socket = 0;
|
||||
socket = new TcpSocket();
|
||||
}
|
||||
|
||||
~DownloadTask()
|
||||
{
|
||||
if (0 != socket)
|
||||
{
|
||||
delete socket;
|
||||
}
|
||||
|
||||
socket = 0;
|
||||
}
|
||||
|
||||
int Start(const char* url, char** buffer, unsigned int* length)
|
||||
{
|
||||
if (0 == socket)
|
||||
{
|
||||
LogError("[DownloadTask::Start] invalid socket, url: %s.\n", url);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret = socket->Open();
|
||||
if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
HttpUrl httpUrl = HttpUtils::ParseUrl(url);
|
||||
|
||||
ret = socket->Connect(httpUrl.host, httpUrl.port);
|
||||
if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = socket->Send(httpUrl.request, strlen(httpUrl.request));
|
||||
if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
char dummy[MAXBUFFERSIZE];
|
||||
ret = socket->Recv(dummy, MAXBUFFERSIZE); // receive 1st package.
|
||||
if (ret <= 0)
|
||||
{
|
||||
LogError("[DownloadTask::Start] recv head 0 bytes, url: %s.\n", url);
|
||||
return -1;
|
||||
}
|
||||
|
||||
dummy[ret] = '\0';
|
||||
|
||||
HttpHead httpHead = HttpUtils::ParseHead(dummy, strlen(dummy));
|
||||
if (HS_FAIL == httpHead.httpState)
|
||||
{
|
||||
LogError("[DownloadTask::Start] recv failed, url: %s.\n", url);
|
||||
return -1;
|
||||
}
|
||||
else if (HS_RETRY == httpHead.httpState)
|
||||
{
|
||||
LogWarn("[DownloadTask::Start] retry, url: %s.\n", url);
|
||||
return Start(url, buffer, length);
|
||||
}
|
||||
else if (HS_REDIR == httpHead.httpState)
|
||||
{
|
||||
LogWarn("[DownloadTask::Start] redir, url: %s, location: %s.\n", url, httpHead.location);
|
||||
return Start(httpHead.location, buffer, length);
|
||||
}
|
||||
|
||||
*length = httpHead.contentLength;
|
||||
*buffer = new char[*length];
|
||||
memset(*buffer, 0, *length);
|
||||
unsigned int offset = 0;
|
||||
|
||||
if (ret > httpHead.headLength)
|
||||
{
|
||||
memcpy((*buffer) + offset, dummy + httpHead.headLength, ret - httpHead.headLength);
|
||||
offset += ret - httpHead.headLength;
|
||||
}
|
||||
|
||||
while ((ret = socket->Recv(dummy, MAXBUFFERSIZE)) >= 0)
|
||||
{
|
||||
LogPrompt("[DownloadTask::Start] received: %d, %d / %d bytes.\n", ret, offset, *length);
|
||||
|
||||
if (offset >= *length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (0 == ret)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (offset + ret > *length)
|
||||
{
|
||||
ret = *length - offset;
|
||||
}
|
||||
|
||||
memcpy((*buffer) + offset, dummy, ret);
|
||||
|
||||
offset += ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
TcpSocket* socket;
|
||||
};
|
||||
*/
|
||||
#endif // _DOWNLOADTASK_H_
|
@ -1,249 +0,0 @@
|
||||
/************************************************
|
||||
*
|
||||
* file : HttpUtils.h
|
||||
* author: bobding
|
||||
* date : 2014-09-25
|
||||
* detail:
|
||||
*
|
||||
************************************************/
|
||||
|
||||
#ifndef _HTTPUTILS_H_
|
||||
#define _HTTPUTILS_H_
|
||||
|
||||
#define MAXURLLENGTH 1024
|
||||
|
||||
struct HttpUrl
|
||||
{
|
||||
char host[MAXURLLENGTH]; // host
|
||||
char file[MAXURLLENGTH]; // file
|
||||
unsigned short port; // port
|
||||
char request[MAXURLLENGTH]; // request
|
||||
};
|
||||
|
||||
enum HttpState
|
||||
{
|
||||
HS_FAIL,
|
||||
HS_SUCC,
|
||||
HS_RETRY,
|
||||
HS_REDIR,
|
||||
};
|
||||
|
||||
struct HttpHead
|
||||
{
|
||||
unsigned int httpCode; // http code
|
||||
enum HttpState httpState; // state
|
||||
unsigned int contentLength; // content length
|
||||
unsigned int headLength; // head length
|
||||
char location[MAXURLLENGTH];// redirection
|
||||
};
|
||||
|
||||
static struct HttpUrl ParseUrl(const char* url)
|
||||
{
|
||||
struct HttpUrl httpUrl;
|
||||
memset(&httpUrl, 0, sizeof(struct HttpUrl));
|
||||
|
||||
unsigned int length = strlen(url);
|
||||
if (length > MAXURLLENGTH)
|
||||
{
|
||||
LogError("[HttpUtils::ParseUrl] url is too long, url: %s.\n", url);
|
||||
return httpUrl;
|
||||
}
|
||||
|
||||
memcpy(httpUrl.host, url, strlen(url));
|
||||
|
||||
const char* s = strchr(httpUrl.host, '/');
|
||||
if (0 != s)
|
||||
{
|
||||
httpUrl.host[s - httpUrl.host] = '\0';
|
||||
s++;
|
||||
memcpy(httpUrl.file, s, strlen(s));
|
||||
}
|
||||
|
||||
s = strchr(httpUrl.host, ':');
|
||||
if (0 != s)
|
||||
{
|
||||
httpUrl.host[s - httpUrl.host] = '\0';
|
||||
s++;
|
||||
httpUrl.port = (unsigned short)atoi(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
httpUrl.port = 80;
|
||||
}
|
||||
|
||||
snprintf(httpUrl.request, MAXURLLENGTH - 1,
|
||||
"GET /%s HTTP/1.1\r\n"
|
||||
"Host: %s\r\n\r\n",
|
||||
httpUrl.file, httpUrl.host);
|
||||
|
||||
LogPrompt("[HttpUtils::ParseUrl] host: %s, file: %s, port: %d.\n", httpUrl.host, httpUrl.file, httpUrl.port);
|
||||
return httpUrl;
|
||||
}
|
||||
|
||||
static struct HttpHead ParseHead(const char* buffer, unsigned int length)
|
||||
{
|
||||
struct HttpHead httpHead;
|
||||
memset(&httpHead, 0, sizeof(struct HttpHead));
|
||||
|
||||
const char* s = strstr(buffer, "HTTP/1.1 ");
|
||||
if (0 != s)
|
||||
{
|
||||
httpHead.httpCode = (unsigned int)atoi(s + strlen("HTTP/1.1 "));
|
||||
}
|
||||
|
||||
if (200 == httpHead.httpCode || 206 == httpHead.httpCode)
|
||||
{
|
||||
httpHead.httpState = HS_SUCC;
|
||||
}
|
||||
else if (201 == httpHead.httpCode)
|
||||
{
|
||||
httpHead.httpState = HS_REDIR;
|
||||
}
|
||||
else if (202 == httpHead.httpCode)
|
||||
{
|
||||
httpHead.httpState = HS_RETRY;
|
||||
}
|
||||
else if (httpHead.httpCode >= 300 && httpHead.httpCode < 400)
|
||||
{
|
||||
httpHead.httpState = HS_REDIR;
|
||||
}
|
||||
else
|
||||
{
|
||||
httpHead.httpState = HS_FAIL;
|
||||
}
|
||||
|
||||
if (HS_REDIR == httpHead.httpState && 0 != (s = strstr(buffer, "Location: ")))
|
||||
{
|
||||
const char* e = strstr(s, "\r\n");
|
||||
s += strlen("Location: ");
|
||||
memcpy(httpHead.location, s, (0 == e) ? strlen(s) : (e - s));
|
||||
}
|
||||
|
||||
s = strstr(buffer, "Content-Length: ");
|
||||
if (0 != s)
|
||||
{
|
||||
httpHead.contentLength = (unsigned int)atoi(s + strlen("Content-Length: "));
|
||||
}
|
||||
|
||||
s = strstr(buffer, "\r\n\r\n");
|
||||
if (0 != s)
|
||||
{
|
||||
httpHead.headLength = s + strlen("\r\n\r\n") - buffer;
|
||||
}
|
||||
|
||||
LogPrompt("[HttpUtils::ParseHead] httpCode: %d, contentLength: %d, headLength: %d.\n",
|
||||
httpHead.httpCode, httpHead.contentLength, httpHead.headLength);
|
||||
|
||||
return httpHead;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
class HttpUtils
|
||||
{
|
||||
public:
|
||||
static HttpUrl ParseUrl(const char* url)
|
||||
{
|
||||
HttpUrl httpUrl;
|
||||
memset(&httpUrl, 0, sizeof(HttpUrl));
|
||||
|
||||
unsigned int length = strlen(url);
|
||||
if (length > MAXURLLENGTH)
|
||||
{
|
||||
LogError("[HttpUtils::ParseUrl] url is too long, url: %s.\n", url);
|
||||
return httpUrl;
|
||||
}
|
||||
|
||||
memcpy(httpUrl.host, url, strlen(url));
|
||||
|
||||
const char* s = strchr(httpUrl.host, '/');
|
||||
if (0 != s)
|
||||
{
|
||||
httpUrl.host[s - httpUrl.host] = '\0';
|
||||
s++;
|
||||
memcpy(httpUrl.file, s, strlen(s));
|
||||
}
|
||||
|
||||
s = strchr(httpUrl.host, ':');
|
||||
if (0 != s)
|
||||
{
|
||||
httpUrl.host[s - httpUrl.host] = '\0';
|
||||
s++;
|
||||
httpUrl.port = (unsigned short)atoi(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
httpUrl.port = 80;
|
||||
}
|
||||
|
||||
snprintf(httpUrl.request, MAXURLLENGTH - 1,
|
||||
"GET /%s HTTP/1.1\r\n"
|
||||
"Host: %s\r\n\r\n",
|
||||
httpUrl.file, httpUrl.host);
|
||||
|
||||
LogPrompt("[HttpUtils::ParseUrl] host: %s, file: %s, port: %d.\n", httpUrl.host, httpUrl.file, httpUrl.port);
|
||||
|
||||
return httpUrl;
|
||||
}
|
||||
|
||||
static HttpHead ParseHead(const char* buffer, unsigned int length)
|
||||
{
|
||||
HttpHead httpHead;
|
||||
memset(&httpHead, 0, sizeof(HttpHead));
|
||||
|
||||
const char* s = strstr(buffer, "HTTP/1.1 ");
|
||||
if (0 != s)
|
||||
{
|
||||
httpHead.httpCode = (unsigned int)atoi(s + strlen("HTTP/1.1 "));
|
||||
}
|
||||
|
||||
if (200 == httpHead.httpCode || 206 == httpHead.httpCode)
|
||||
{
|
||||
httpHead.httpState = HS_SUCC;
|
||||
}
|
||||
else if (201 == httpHead.httpCode)
|
||||
{
|
||||
httpHead.httpState = HS_REDIR;
|
||||
}
|
||||
else if (202 == httpHead.httpCode)
|
||||
{
|
||||
httpHead.httpState = HS_RETRY;
|
||||
}
|
||||
else if (httpHead.httpCode >= 300 && httpHead.httpCode < 400)
|
||||
{
|
||||
httpHead.httpState = HS_REDIR;
|
||||
}
|
||||
else
|
||||
{
|
||||
httpHead.httpState = HS_FAIL;
|
||||
}
|
||||
|
||||
if (HS_REDIR == httpHead.httpState && 0 != (s = strstr(buffer, "Location: ")))
|
||||
{
|
||||
const char* e = strstr(s, "\r\n");
|
||||
s += strlen("Location: ");
|
||||
memcpy(httpHead.location, s, (0 == e) ? strlen(s) : (e - s));
|
||||
}
|
||||
|
||||
s = strstr(buffer, "Content-Length: ");
|
||||
if (0 != s)
|
||||
{
|
||||
httpHead.contentLength = (unsigned int)atoi(s + strlen("Content-Length: "));
|
||||
}
|
||||
|
||||
s = strstr(buffer, "\r\n\r\n");
|
||||
if (0 != s)
|
||||
{
|
||||
httpHead.headLength = s + strlen("\r\n\r\n") - buffer;
|
||||
}
|
||||
|
||||
LogPrompt("[HttpUtils::ParseHead] httpCode: %d, contentLength: %d, headLength: %d.\n",
|
||||
httpHead.httpCode, httpHead.contentLength, httpHead.headLength);
|
||||
|
||||
return httpHead;
|
||||
}
|
||||
};
|
||||
*/
|
||||
|
||||
#endif // _HTTPUTILS_H_
|
@ -1,16 +0,0 @@
|
||||
Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
|
||||
Copyright (c) 2013-2015 Cesanta Software Limited
|
||||
All rights reserved
|
||||
|
||||
This code is dual-licensed: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 as
|
||||
published by the Free Software Foundation. For the terms of this
|
||||
license, see <http://www.gnu.org/licenses>.
|
||||
|
||||
You are free to use this code under the terms of the GNU General
|
||||
Public License, 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.
|
||||
|
||||
Alternatively, you can license this code under a commercial
|
||||
license, as set out in <http://cesanta.com/>.
|
@ -1,322 +0,0 @@
|
||||
/************************************************
|
||||
*
|
||||
* file : Log.h
|
||||
* author: bobding
|
||||
* date : 2014-09-24
|
||||
* detail:
|
||||
*
|
||||
************************************************/
|
||||
|
||||
#ifndef _LOG_H_
|
||||
#define _LOG_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
#include <memory.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#define LogCritical Critical
|
||||
#define LogError Error
|
||||
#define LogWarn Warn
|
||||
#define LogPrompt Prompt
|
||||
|
||||
#define LOGPATH "./1.log"
|
||||
|
||||
FILE* handle;
|
||||
/*
|
||||
static Log* Instance()
|
||||
{
|
||||
static Log instance;
|
||||
return &instance;
|
||||
}
|
||||
*/
|
||||
|
||||
const char* FormatedTime()
|
||||
{
|
||||
static char strDate[128];
|
||||
memset(strDate, 0, sizeof(strDate));
|
||||
|
||||
struct timeval tv;
|
||||
int ret = gettimeofday(&tv, 0);
|
||||
if (ret < 0)
|
||||
{
|
||||
return strDate;
|
||||
}
|
||||
|
||||
struct tm* pt = localtime(&tv.tv_sec);
|
||||
snprintf(strDate, 127, "%d-%02d-%02d %02d:%02d:%02d.%03d", pt->tm_year + 1900, pt->tm_mon + 1, pt->tm_mday, pt->tm_hour, pt->tm_min, pt->tm_sec, (int)(tv.tv_usec / 1000));
|
||||
|
||||
return strDate;
|
||||
}
|
||||
|
||||
void initLog()
|
||||
{
|
||||
handle = 0;
|
||||
if (-1 != access(LOGPATH, F_OK))
|
||||
{
|
||||
remove(LOGPATH);
|
||||
}
|
||||
handle = fopen(LOGPATH, "w");
|
||||
}
|
||||
|
||||
void destroyLog()
|
||||
{
|
||||
if (0 != handle)
|
||||
{
|
||||
fclose(handle);
|
||||
}
|
||||
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
// flush every item
|
||||
int Critical(const char* fmt, ...)
|
||||
{
|
||||
initLog();
|
||||
static char buffer[128 + 1024];
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
snprintf(buffer, 128, "%s [%-8s] ", FormatedTime(), "Critical");
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int length = vsnprintf((char*)(buffer+strlen(buffer)), 1023, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
fwrite(buffer, strlen(buffer), 1, handle);
|
||||
fflush(handle);
|
||||
|
||||
printf("%s", buffer);
|
||||
destroyLog();
|
||||
return length;
|
||||
}
|
||||
|
||||
// flush every item
|
||||
int Error(const char* fmt, ...)
|
||||
{
|
||||
initLog();
|
||||
if (0 == handle)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static char buffer[128 + 1024];
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
snprintf(buffer, 128, "%s [%-8s] ", FormatedTime(), "Error");
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int length = vsnprintf((char*)(buffer+strlen(buffer)), 1023, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
fwrite(buffer, strlen(buffer), 1, handle);
|
||||
fflush(handle);
|
||||
|
||||
printf("%s", buffer);
|
||||
destroyLog();
|
||||
return length;
|
||||
}
|
||||
|
||||
// flush every 100 items, careless lost
|
||||
int Warn(const char* fmt, ...)
|
||||
{
|
||||
initLog();
|
||||
static unsigned int maxCnt = 100;
|
||||
static unsigned int curCnt = 0;
|
||||
|
||||
if (0 == handle)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static char buffer[128 + 1024];
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
snprintf(buffer, 128, "%s [%-8s] ", FormatedTime(), "Warn");
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int length = vsnprintf((char*)(buffer+strlen(buffer)), 1023, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
fwrite(buffer, strlen(buffer), 1, handle);
|
||||
|
||||
if (++curCnt >= maxCnt)
|
||||
{
|
||||
fflush(handle);
|
||||
curCnt = 0;
|
||||
}
|
||||
|
||||
printf("%s", buffer);
|
||||
destroyLog();
|
||||
return length;
|
||||
}
|
||||
|
||||
// stdout
|
||||
int Prompt(const char* fmt, ...)
|
||||
{
|
||||
initLog();
|
||||
static char buffer[128 + 1024];
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
snprintf(buffer, 128, "%s [%-8s] ", FormatedTime(), "Prompt");
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int length = vsnprintf((char*)(buffer+strlen(buffer)), 1023, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
printf("%s", buffer);
|
||||
destroyLog();
|
||||
return length;
|
||||
}
|
||||
|
||||
/*
|
||||
class Log
|
||||
{
|
||||
private:
|
||||
FILE* handle;
|
||||
|
||||
public:
|
||||
static Log* Instance()
|
||||
{
|
||||
static Log instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
public:
|
||||
Log() : handle(0)
|
||||
{
|
||||
if (-1 != access(LOGPATH, F_OK))
|
||||
{
|
||||
remove(LOGPATH);
|
||||
}
|
||||
|
||||
handle = fopen(LOGPATH, "w");
|
||||
}
|
||||
|
||||
~Log()
|
||||
{
|
||||
if (0 != handle)
|
||||
{
|
||||
fclose(handle);
|
||||
}
|
||||
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
// flush every item
|
||||
int Critical(const char* fmt, ...)
|
||||
{
|
||||
static char buffer[128 + 1024];
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
snprintf(buffer, 128, "%s [%-8s] ", FormatedTime(), "Critical");
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int length = vsnprintf((char*)(buffer+strlen(buffer)), 1023, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
fwrite(buffer, strlen(buffer), 1, handle);
|
||||
fflush(handle);
|
||||
|
||||
printf("%s", buffer);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
// flush every item
|
||||
int Error(const char* fmt, ...)
|
||||
{
|
||||
if (0 == handle)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static char buffer[128 + 1024];
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
snprintf(buffer, 128, "%s [%-8s] ", FormatedTime(), "Error");
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int length = vsnprintf((char*)(buffer+strlen(buffer)), 1023, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
fwrite(buffer, strlen(buffer), 1, handle);
|
||||
fflush(handle);
|
||||
|
||||
printf("%s", buffer);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
// flush every 100 items, careless lost
|
||||
int Warn(const char* fmt, ...)
|
||||
{
|
||||
static unsigned int maxCnt = 100;
|
||||
static unsigned int curCnt = 0;
|
||||
|
||||
if (0 == handle)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static char buffer[128 + 1024];
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
snprintf(buffer, 128, "%s [%-8s] ", FormatedTime(), "Warn");
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int length = vsnprintf((char*)(buffer+strlen(buffer)), 1023, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
fwrite(buffer, strlen(buffer), 1, handle);
|
||||
|
||||
if (++curCnt >= maxCnt)
|
||||
{
|
||||
fflush(handle);
|
||||
curCnt = 0;
|
||||
}
|
||||
|
||||
printf("%s", buffer);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
// stdout
|
||||
int Prompt(const char* fmt, ...)
|
||||
{
|
||||
static char buffer[128 + 1024];
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
snprintf(buffer, 128, "%s [%-8s] ", FormatedTime(), "Prompt");
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int length = vsnprintf((char*)(buffer+strlen(buffer)), 1023, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
printf("%s", buffer);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
protected:
|
||||
const char* FormatedTime()
|
||||
{
|
||||
static char strDate[128];
|
||||
memset(strDate, 0, sizeof(strDate));
|
||||
|
||||
struct timeval tv;
|
||||
int ret = gettimeofday(&tv, 0);
|
||||
if (ret < 0)
|
||||
{
|
||||
return strDate;
|
||||
}
|
||||
|
||||
struct tm* pt = localtime(&tv.tv_sec);
|
||||
snprintf(strDate, 127, "%d-%02d-%02d %02d:%02d:%02d.%03d", pt->tm_year + 1900, pt->tm_mon + 1, pt->tm_mday, pt->tm_hour, pt->tm_min, pt->tm_sec, (int)(tv.tv_usec / 1000));
|
||||
|
||||
return strDate;
|
||||
}
|
||||
};
|
||||
*/
|
||||
#endif // _LOG_H_
|
@ -1,78 +0,0 @@
|
||||
# <img src="http://cesanta.com/images/mongoose_logo.png" width="64" height="64"> Mongoose Web Server
|
||||
|
||||
[![Join the chat at https://gitter.im/cesanta/mongoose](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/cesanta/mongoose?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
Mongoose is the most easy to use web server on the planet. A web server of choice for Web developers (PHP, Ruby, Python, etc) and Web designers.
|
||||
|
||||
Mongoose is built on top of Libmongoose embedded library, which can turn
|
||||
anything into a web server in 5 minutes worth of effort and few lines of code.
|
||||
Libmongoose is used to serve Web GUI on embedded devices, implement RESTful
|
||||
services, RPC frameworks (e.g. JSON-RPC), handle telemetry data exchange, and
|
||||
perform many other tasks in various different industries including aerospace,
|
||||
manufacturing, finance, research, automotive, gaming, IT.
|
||||
|
||||
|
||||
* [Mailing list](http://groups.google.com/group/mongoose-users)
|
||||
* [Downloads](http://cesanta.com/products.shtml)
|
||||
* [Documentation](http://cesanta.com/docs.shtml)
|
||||
|
||||
Check out Fossa - our [embedded multi-protocol library](https://github.com/cesanta/fossa) with TCP,UDP,HTTP,Websocket,MQTT,DNS support, designed for Internet Of Things!
|
||||
|
||||
# Features
|
||||
|
||||
- Works on Windows, Mac, UNIX/Linux, iPhone, Android eCos, QNX
|
||||
and many other platforms
|
||||
- CGI, SSI, SSL, Digest auth, Websocket, WEbDAV, Resumed download,
|
||||
URL rewrite, file blacklist
|
||||
- Custom error pages, Virtual hosts, IP-based ACL, Windows service,
|
||||
HTTP/HTTPS client
|
||||
- Simple and clean
|
||||
[embedding API](https://github.com/cesanta/mongoose/blob/master/mongoose.h).
|
||||
The source is in single
|
||||
[mongoose.c](https://github.com/cesanta/mongoose/blob/master/mongoose.c) file
|
||||
to make embedding easy
|
||||
- Extremely lightweight, has a core of under 40kB and tiny runtime footprint
|
||||
- Asynchronous, non-blocking core supporting single- or multi-threaded usage
|
||||
- On the market since 2004 with over 1 million cumulative downloads
|
||||
- Stable, mature and tested, has several man-years invested
|
||||
in continuous improvement and refinement
|
||||
|
||||
# Screenshots
|
||||
|
||||
Download, double-click to start, run browser -- that's all!
|
||||
|
||||
![shot1](http://cesanta.com/images/tut_sharing/tut1.png)
|
||||
![shot2](http://cesanta.com/images/tut_sharing/tut2.png)
|
||||
|
||||
![shot3](http://cesanta.com/images/tut_sharing/tut3.png)
|
||||
![shot4](http://cesanta.com/images/tut_sharing/tut4.png)
|
||||
|
||||
# Contributions
|
||||
|
||||
People who have agreed to the
|
||||
[Cesanta CLA](http://cesanta.com/contributors_la.html)
|
||||
can make contributions. Note that the CLA isn't a copyright
|
||||
_assigment_ but rather a copyright _license_.
|
||||
You retain the copyright on your contributions.
|
||||
|
||||
# Licensing
|
||||
|
||||
Mongoose is released under commercial and
|
||||
[GNU GPL v.2](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html) open
|
||||
source licenses. The GPLv2 open source License does not generally permit
|
||||
incorporating this software into non-open source programs.
|
||||
For those customers who do not wish to comply with the GPLv2 open
|
||||
source license requirements,
|
||||
[Cesanta](http://cesanta.com) offers a full,
|
||||
royalty-free commercial license and professional support
|
||||
without any of the GPL restrictions.
|
||||
|
||||
# Other products by Cesanta
|
||||
|
||||
- [Fossa](http://github.com/cesanta/fossa) - Multi-protocol networking library
|
||||
- [SSL Wrapper](https://github.com/cesanta/ssl_wrapper) - application to
|
||||
secure network communications
|
||||
- [Frozen](https://github.com/cesanta/frozen) - JSON parser and generator
|
||||
- [SLRE](https://github.com/cesanta/slre) - Super Light Regular Expression
|
||||
library
|
||||
- [V7](https://github.com/cesanta/v7) - Embedded JavaScript engine
|
@ -1,246 +0,0 @@
|
||||
/************************************************
|
||||
*
|
||||
* file : TcpSocket.h
|
||||
* author: bobding
|
||||
* date : 2014-09-25
|
||||
* detail:
|
||||
*
|
||||
************************************************/
|
||||
|
||||
#ifndef _TCPSOCKET_H_
|
||||
#define _TCPSOCKET_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
#include "Log.h"
|
||||
|
||||
int sockfd;
|
||||
|
||||
void initTcpSocket(){
|
||||
sockfd = -1;
|
||||
}
|
||||
|
||||
// return >=0 means success, otherwise failed.
|
||||
int socketOpen(){
|
||||
sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (sockfd < 0)
|
||||
{
|
||||
LogError("[Socket::Open] create socket failed: %s.\n", strerror(errno));
|
||||
}
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
// host: 127.0.0.1 or www.qq.com
|
||||
// return 0 means success, otherwise failed.
|
||||
int socketConnect(const char* host, unsigned short port)
|
||||
{
|
||||
if (sockfd < 0)
|
||||
{
|
||||
LogError("[Socket::Connect] invalid sockfd.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
|
||||
struct hostent* h = gethostbyname(host);
|
||||
if (0 == h)
|
||||
{
|
||||
LogError("[Socket::Connect] gethostbyname failed: %s.\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr.sin_addr = *(struct in_addr*)h->h_addr_list[0];
|
||||
|
||||
int ret = connect(sockfd, (const struct sockaddr*)&addr, sizeof(addr));
|
||||
if (ret < 0)
|
||||
{
|
||||
LogError("[Socket::Connect] connect failed: %s.\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// return send bytes, -1 means failed.
|
||||
int socketSend(const char* buffer, unsigned int length)
|
||||
{
|
||||
if (sockfd < 0)
|
||||
{
|
||||
LogError("[Socket::Send] invalid sockfd.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret = send(sockfd, buffer, length, 0);
|
||||
if (ret < 0)
|
||||
{
|
||||
LogError("[Socket::Send] send failed: %s.\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// return recv bytes, -1 means failed.
|
||||
int socketRecv(char* buffer, unsigned int length)
|
||||
{
|
||||
if (sockfd < 0)
|
||||
{
|
||||
LogError("[Socket::Recv] invalid sockfd.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret = recv(sockfd, buffer, length, 0);
|
||||
if (ret < 0)
|
||||
{
|
||||
LogError("[Socket::Recv] recv failed: %s.\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// return 0 success, otherwise failed.
|
||||
int Close()
|
||||
{
|
||||
int ret = close(sockfd);
|
||||
if (ret < 0)
|
||||
{
|
||||
LogError("[Socket::Close] close socket failed: %s.\n", strerror(errno));
|
||||
}
|
||||
|
||||
sockfd = -1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int GetSocket()
|
||||
{
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
/*
|
||||
class TcpSocket
|
||||
{
|
||||
public:
|
||||
TcpSocket()
|
||||
{
|
||||
sockfd = -1;
|
||||
}
|
||||
|
||||
// return >=0 means success, otherwise failed.
|
||||
int Open()
|
||||
{
|
||||
sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (sockfd < 0)
|
||||
{
|
||||
LogError("[Socket::Open] create socket failed: %s.\n", strerror(errno));
|
||||
}
|
||||
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
// host: 127.0.0.1 or www.qq.com
|
||||
// return 0 means success, otherwise failed.
|
||||
int Connect(const char* host, unsigned short port)
|
||||
{
|
||||
if (sockfd < 0)
|
||||
{
|
||||
LogError("[Socket::Connect] invalid sockfd.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
|
||||
struct hostent* h = gethostbyname(host);
|
||||
if (0 == h)
|
||||
{
|
||||
LogError("[Socket::Connect] gethostbyname failed: %s.\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr.sin_addr = *(struct in_addr*)h->h_addr_list[0];
|
||||
|
||||
int ret = connect(sockfd, (const struct sockaddr*)&addr, sizeof(addr));
|
||||
if (ret < 0)
|
||||
{
|
||||
LogError("[Socket::Connect] connect failed: %s.\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// return send bytes, -1 means failed.
|
||||
int Send(const char* buffer, unsigned int length)
|
||||
{
|
||||
if (sockfd < 0)
|
||||
{
|
||||
LogError("[Socket::Send] invalid sockfd.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret = send(sockfd, buffer, length, 0);
|
||||
if (ret < 0)
|
||||
{
|
||||
LogError("[Socket::Send] send failed: %s.\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// return recv bytes, -1 means failed.
|
||||
int Recv(char* buffer, unsigned int length)
|
||||
{
|
||||
if (sockfd < 0)
|
||||
{
|
||||
LogError("[Socket::Recv] invalid sockfd.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret = recv(sockfd, buffer, length, 0);
|
||||
if (ret < 0)
|
||||
{
|
||||
LogError("[Socket::Recv] recv failed: %s.\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// return 0 success, otherwise failed.
|
||||
int Close()
|
||||
{
|
||||
int ret = close(sockfd);
|
||||
if (ret < 0)
|
||||
{
|
||||
LogError("[Socket::Close] close socket failed: %s.\n", strerror(errno));
|
||||
}
|
||||
|
||||
sockfd = -1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int GetSocket()
|
||||
{
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
private:
|
||||
int sockfd;
|
||||
};
|
||||
*/
|
||||
|
||||
#endif // _TCPSOCKET_H_
|
@ -1,249 +0,0 @@
|
||||
# Mongoose API Reference
|
||||
|
||||
struct mg_server *mg_create_server(void *server_param, mg_handler_t handler);
|
||||
|
||||
Creates web server instance. Returns opaque instance pointer, or NULL if
|
||||
there is not enough memory. `server_param`: Could be any pointer, or NULL.
|
||||
This pointer will be passed
|
||||
to the callback functions as `struct mg_connection::server_param` field.
|
||||
A common use case is to pass `this` pointer of the C++ wrapper class
|
||||
as `user_param`, to let the callback get the pointer to the C++ object.
|
||||
|
||||
Note that this function doesn't make the
|
||||
server instance to serve. Serving is done by `mg_poll_server()` function.
|
||||
Mongoose has single-threaded, event-driven, asynchronous, non-blocking core.
|
||||
When server instance is created, it contains an information about
|
||||
the configuration and the state of each connection.
|
||||
Server instance is capable on listening on only one port. After creation,
|
||||
`struct mg_server` has a list
|
||||
of active connections and configuration parameters.
|
||||
|
||||
Side-effect: on UNIX, `mg_create_server()` ignores SIGPIPE signals. If custom
|
||||
processing is required SIGPIPE, signal handler must be set up after
|
||||
calling `mg_create_server()`.
|
||||
|
||||
Important: Mongoose does not install `SIGCHLD` handler. If CGI is used,
|
||||
`SIGCHLD` handler must be set up to reap CGI zombie processes.
|
||||
|
||||
|
||||
void mg_destroy_server(struct mg_server **server);
|
||||
|
||||
Deallocates web server instance, closes all pending connections, and makes
|
||||
server pointer a NULL pointer.
|
||||
|
||||
const char mg_set_option(struct mg_server *server, const char *name,
|
||||
const char *value);
|
||||
|
||||
Sets a particular server option. Note that at least one option,
|
||||
`listening_port`, must be specified. To serve static files, `document_root`
|
||||
must be specified too. If `document_root` option is left unset, Mongoose
|
||||
will not access filesystem at all. `mg_set_option()` returns NULL if option was
|
||||
set successfully, otherwise it returns human-readable error string. It is
|
||||
allowed to call `mg_set_option()` by the same thread that does
|
||||
`mg_poll_server()` (Mongoose thread) and change server configuration while it
|
||||
is serving, in between `mg_poll_server()` calls.
|
||||
|
||||
int mg_poll_server(struct mg_server *server, int milliseconds);
|
||||
|
||||
Performs one iteration of IO loop by iterating over all
|
||||
active connections, performing `select()` syscall on all sockets with a timeout
|
||||
of `milliseconds`. When `select()` returns, Mongoose
|
||||
does an IO for each socket that has data to be sent or received. Application
|
||||
code must call `mg_poll_server()` in a loop. It is an error to have more then
|
||||
one thread calling `mg_poll_server()`, `mg_set_option()` or any other function
|
||||
that take `struct mg_server *` parameter. Mongoose does not
|
||||
mutex-protect `struct mg_server *`, therefore only single thread
|
||||
(Mongoose thread) should make Mongoose calls.
|
||||
|
||||
`mg_poll_server()` calls user-specified event handler when certain events
|
||||
occur. Sequence of events for the accepted connection is this:
|
||||
|
||||
* `MG_AUTH` - Mongoose asks whether this connection is authorized. If event
|
||||
handler returns `MG_FALSE`, then Mongoose does not serve the request but
|
||||
sends authorization request to the client. If `MG_TRUE` is returned,
|
||||
then Mongoose continues on with the request.
|
||||
* `MG_REQUEST` - Mongoose asks event handler to serve the request. If
|
||||
event handler serves the request by sending a reply,
|
||||
it should return `MG_TRUE`. Otherwise,
|
||||
it should return `MG_FALSE` which tells Mongoose that request is not
|
||||
served and Mongoose should serve it. For example, event handler might
|
||||
choose to serve only RESTful API requests with URIs that start with
|
||||
certain prefix, and let Mongoose serve all static files.
|
||||
If event handler decides to serve the request, but doesn't have
|
||||
all the data at the moment, it should return `MG_MORE`. That tells
|
||||
Mongoose to keep the connection open after callback returns.
|
||||
|
||||
`mg_connection::connection_param` pointer is a placeholder to keep
|
||||
user-specific data. For example, handler could decide to open a DB
|
||||
connection and store DB connection handle in `connection_param`.
|
||||
* `MG_POLL` is sent to every connection on every iteration of
|
||||
`mg_poll_server()`. Event handler should return `MG_FALSE` to ignore
|
||||
this event. If event handler returns `MG_TRUE`, then Mongoose assumes
|
||||
that event handler has finished sending data, and Mongoose will
|
||||
close the connection.
|
||||
* `MG_HTTP_ERROR` sent when Mongoose is about to send HTTP error back
|
||||
to the client. Event handler can choose to send a reply itself, in which
|
||||
case event handler must return `MG_TRUE`. Otherwise, event handler must
|
||||
return `MG_FALSE`.
|
||||
* `MG_CLOSE` is sent when the connection is closed. This event is used
|
||||
to cleanup per-connection state stored in `connection_param`
|
||||
if it was allocated. Event handler return value is ignored.
|
||||
|
||||
Sequence of events for the client connection is this:
|
||||
|
||||
* `MG_CONNECT` sent when Mongoose has connected to the remote host.
|
||||
This event is sent to the connection initiated by `mg_connect()` call.
|
||||
Connection status is held in `mg_connection::status_code`: if zero,
|
||||
then connection was successful, otherwise connection was not established.
|
||||
User should send a request upon successful connection.
|
||||
Event handler should return `MG_TRUE` if connection was successful and
|
||||
HTTP request has been sent. Otherwise, it should send `MG_FALSE`.
|
||||
* `MG_REPLY` is sent when response has been received from the remote host.
|
||||
If event handler sends another request, then it should return `MG_TRUE`.
|
||||
Otherwise it should return `MG_FALSE` and Mongoose will close the connection.
|
||||
* `MG_CLOSE` same as for the accepted connection.
|
||||
|
||||
|
||||
When mongoose buffers in HTTP request and successfully parses it, it sends
|
||||
`MG_REQUEST` event for GET requests immediately. For POST requests,
|
||||
Mongoose delays the call until the whole POST request is buffered in memory.
|
||||
POST data is available to the callback as `struct mg_connection::content`,
|
||||
and POST data length is in `struct mg_connection::content_len`.
|
||||
|
||||
Note that websocket connections are treated the same way. Mongoose buffers
|
||||
websocket frame in memory, and calls event handler when frame is fully
|
||||
buffered. Frame data is available `struct mg_connection::content`, and
|
||||
data length is in `struct mg_connection::content_len`, i.e. very similar to
|
||||
the POST request. `struct mg_connection::is_websocket` flag indicates
|
||||
whether the request is websocket or not. Also, for websocket requests,
|
||||
there is `struct mg_connection::wsbits` field which contains first byte
|
||||
of the websocket frame which URI handler can examine. Note that to
|
||||
reply to the websocket client, `mg_websocket_write()` should be used.
|
||||
To reply to the plain HTTP client, `mg_write_data()` should be used.
|
||||
|
||||
Return value: number of active connections.
|
||||
|
||||
|
||||
const char **mg_get_valid_option_names(void);
|
||||
|
||||
Returns a NULL-terminated array of option names and their default values.
|
||||
There are two entries per option in an array: an option name followed by a
|
||||
default value. A default value could be NULL. A NULL name indicates an end
|
||||
of the array.
|
||||
|
||||
const char *mg_get_option(const struct mg_server *server, const char *name);
|
||||
|
||||
Returns the value of particular configuration parameter. If
|
||||
given parameter name is not valid, NULL is returned. For valid names, return
|
||||
value is guaranteed to be non-NULL. If parameter is not set, zero-length string
|
||||
is returned.
|
||||
|
||||
void mg_wakeup_server_ex(struct mg_server *, mg_handler_t func,
|
||||
const char *fmt, ...);
|
||||
|
||||
Sends string message to a server. Function `func` is called for every active
|
||||
connection. String message is passed in `struct mg_connection::callback_param`.
|
||||
This function is designed to push data to the connected clients, and
|
||||
can be called from any thread. There is a limitation on the length of
|
||||
the message, currently at 8 kilobytes.
|
||||
|
||||
void mg_send_status(struct mg_connection *, int status_code);
|
||||
void mg_send_header(struct mg_connection *, const char *name,
|
||||
const char *value);
|
||||
void mg_send_data(struct mg_connection *, const void *data, int data_len);
|
||||
void mg_printf_data(struct mg_connection *, const char *format, ...);
|
||||
|
||||
These functions are used to construct a response to the client. HTTP response
|
||||
consists of three parts: a status line, zero or more HTTP headers,
|
||||
a response body. Mongoose provides functions for all three parts:
|
||||
* `mg_send_status()` is used to create status line. This function can be
|
||||
called zero or once. If `mg_send_status()` is not called, then Mongoose
|
||||
will send status 200 (success) implicitly.
|
||||
* `mg_send_header()` adds HTTP header to the response. This function could
|
||||
be called zero or more times.
|
||||
* `mg_send_data()` and `mg_printf_data()` are used to send data to the
|
||||
client. Note that Mongoose adds `Transfer-Encoding: chunked` header
|
||||
implicitly, and sends data in chunks. Therefore, it is not necessary to
|
||||
set `Content-Length` header. Note that `mg_send_data()` and
|
||||
`mg_printf_data()` do not send data immediately. Instead, they spool
|
||||
data in memory, and Mongoose sends that data later after URI handler
|
||||
returns. If data to be sent is huge, an URI handler might
|
||||
send data in pieces by saving state in
|
||||
`struct mg_connection::connection_param` variable and returning `0`. Then
|
||||
Mongoose will call a handler repeatedly after each socket write.
|
||||
|
||||
<!-- -->
|
||||
|
||||
void mg_send_file(struct mg_connection *, const char *path);
|
||||
|
||||
Tells Mongoose to serve given file. Mongoose handles file according to
|
||||
it's extensions, i.e. Mongoose will invoke CGI script if `path` has CGI
|
||||
extension, it'll render SSI file if `path` has SSI extension, etc. If `path`
|
||||
points to a directory, Mongoose will show directory listing. If this function
|
||||
is used, no calls to `mg_send*` or `mg_printf*` functions must be made, and
|
||||
event handler must return `MG_MORE`.
|
||||
|
||||
size_t mg_websocket_write(struct mg_connection* conn, int opcode,
|
||||
const char *data, size_t data_len);
|
||||
size_t mg_websocket_printf(struct mg_connection* conn, int opcode,
|
||||
const char *fmt, ...);
|
||||
|
||||
|
||||
Similar to `mg_write()` and `mg_printf()`, but wraps the data into a
|
||||
websocket frame with a given websocket `opcode`.
|
||||
|
||||
const char *mg_get_header(const struct mg_connection *, const char *name);
|
||||
|
||||
Get the value of particular HTTP header. This is a helper function.
|
||||
It traverses http_headers array, and if the header is present in the array,
|
||||
returns its value. If it is not present, NULL is returned.
|
||||
|
||||
|
||||
int mg_get_var(const struct mg_connection *conn, const char *var_name,
|
||||
char *buf, size_t buf_len);
|
||||
|
||||
Gets HTTP form variable. Both POST buffer and query string are inspected.
|
||||
Form variable is url-decoded and written to the buffer. On success, this
|
||||
function returns the length of decoded variable. On error, -1 is returned if
|
||||
variable not found, and -2 is returned if destination buffer is too small
|
||||
to hold the variable. Destination buffer is guaranteed to be
|
||||
'\0' - terminated if it is not NULL or zero length.
|
||||
|
||||
int mg_parse_header(const char *hdr, const char *var_name, char *buf,
|
||||
size_t buf_size);
|
||||
|
||||
This function parses HTTP header and fetches given variable's value in a buffer.
|
||||
A header should be like `x=123, y=345, z="other value"`. This function is
|
||||
designed to parse Cookie headers, Authorization headers, and similar. Returns
|
||||
the length of the fetched value, or 0 if variable not found.
|
||||
|
||||
int mg_modify_passwords_file(const char *passwords_file_name,
|
||||
const char *domain,
|
||||
const char *user,
|
||||
const char *password);
|
||||
|
||||
Add, edit or delete the entry in the passwords file.
|
||||
This function allows an application to manipulate .htpasswd files on the
|
||||
fly by adding, deleting and changing user records. This is one of the
|
||||
several ways of implementing authentication on the server side.
|
||||
If password is not NULL, entry is added (or modified if already exists).
|
||||
If password is NULL, entry is deleted.
|
||||
Return: 1 on success, 0 on error.
|
||||
|
||||
|
||||
int mg_parse_multipart(const char *buf, int buf_len,
|
||||
char *var_name, int var_name_len,
|
||||
char *file_name, int file_name_len,
|
||||
const char **data, int *data_len);
|
||||
|
||||
Parses a buffer that contains multipart form data. Stores chunk name
|
||||
in a `var_name` buffer. If chunk is an uploaded file, then `file_name`
|
||||
will have a file name. `data` and `data_len` will point to the chunk data.
|
||||
Returns number of bytes to skip to the next chunk.
|
||||
|
||||
struct mg_connection *mg_connect(struct mg_server *server,
|
||||
const char *host, int port, int use_ssl);
|
||||
|
||||
Create connection to the remote host. Returns `NULL` on error, non-null
|
||||
if the connection has been scheduled for connection. Upon a connection,
|
||||
Mongoose will send `MG_CONNECT` event to the event handler.
|
@ -1,27 +0,0 @@
|
||||
# Mongoose Build on Android
|
||||
|
||||
This is a small guide to help you run mongoose on Android. Currently it is
|
||||
tested on the HTC Wildfire. If you have managed to run it on other devices
|
||||
as well, please comment or drop an email in the mailing list.
|
||||
Note : You dont need root access to run mongoose on Android.
|
||||
|
||||
- Clone Mongoose Git repo
|
||||
- Download the Android NDK from [http://developer.android.com/tools/sdk/ndk/index.html](http://developer.android.com/tools/sdk/ndk/index.html)
|
||||
- Run `/path-to-ndk/ndk-build -C /path/to/mongoose`
|
||||
That should generate mongoose/lib/armeabi/mongoose
|
||||
- Using the adb tool (you need to have Android SDK installed for that),
|
||||
push the generated mongoose binary to `/data/local` folder on device.
|
||||
- From adb shell, navigate to `/data/local` and execute `./mongoose`.
|
||||
- To test if the server is running fine, visit your web-browser and
|
||||
navigate to `http://127.0.0.1:8080` You should see the `Index of /` page.
|
||||
|
||||
![screenshot](http://cesanta.com/images/android_build.png)
|
||||
|
||||
|
||||
Notes:
|
||||
|
||||
- `jni` stands for Java Native Interface. Read up on Android NDK if you want
|
||||
to know how to interact with the native C functions of mongoose in Android
|
||||
Java applications.
|
||||
- TODO: A Java application that interacts with the native binary or a
|
||||
shared library.
|
@ -1,34 +0,0 @@
|
||||
How To Create Basic Website With Mongoose
|
||||
===========================================
|
||||
|
||||
## 1. Create a directory which will contain your website files. For example, on drive `C:\`, create a directory called `my_website`:
|
||||
|
||||
![screenshot](http://cesanta.com/images/tut_basic/tut1.png)
|
||||
|
||||
## 2. Inside `my_website` directory, create a new file called "index". This will be the default web page shown when the website is visited.
|
||||
|
||||
![screenshot](http://cesanta.com/images/tut_basic/tut2.png)
|
||||
|
||||
## 3. Open index file with your favorite editor (for example, Notepad) and enter some HTML code:
|
||||
|
||||
![screenshot](http://cesanta.com/images/tut_basic/tut3.png)
|
||||
|
||||
## 4. Save this file as `index.html`:
|
||||
|
||||
![screenshot](http://cesanta.com/images/tut_basic/tut4.png)
|
||||
|
||||
|
||||
## 5. Download Mongoose executable from http://cesanta.com/mongoose.shtml and copy the executable inside `my_website` directory:
|
||||
|
||||
![screenshot](http://cesanta.com/images/tut_basic/tut5.png)
|
||||
|
||||
## 6. Double-click mongoose executable. An icon will appear on a system tray in the bottom right corner of the desktop:
|
||||
|
||||
![screenshot](http://cesanta.com/images/tut_basic/tut6.png)
|
||||
|
||||
## 7. Click on the mongoose icon and choose "Go to my address" menu:
|
||||
![screenshot](http://cesanta.com/images/tut_basic/tut7.png)
|
||||
|
||||
## 8. A browser will popup displaying `index.html` file. Now, you can expand your website by adding more content.
|
||||
|
||||
![screenshot](http://cesanta.com/images/tut_basic/tut8.png)
|
@ -1,173 +0,0 @@
|
||||
# Mongoose Embedding Guide
|
||||
|
||||
Embedding Mongoose is done in two steps:
|
||||
|
||||
1. Copy
|
||||
[mongoose.c](https://raw.github.com/cesanta/mongoose/master/mongoose.c) and
|
||||
[mongoose.h](https://raw.github.com/cesanta/mongoose/master/mongoose.h)
|
||||
to your application's source tree and include them in the build.
|
||||
2. Somewhere in the application code, call `mg_create_server()` to create
|
||||
a server, configure it with `mg_set_option()` and loop with
|
||||
`mg_poll_server()` until done. Call `mg_destroy_server()` to cleanup.
|
||||
|
||||
Here's a minimal application `app.c` that embeds mongoose:
|
||||
|
||||
#include "mongoose.h"
|
||||
|
||||
int main(void) {
|
||||
struct mg_server *server = mg_create_server(NULL, NULL);
|
||||
mg_set_option(server, "document_root", "."); // Serve current directory
|
||||
mg_set_option(server, "listening_port", "8080"); // Open port 8080
|
||||
|
||||
for (;;) {
|
||||
mg_poll_server(server, 1000); // Infinite loop, Ctrl-C to stop
|
||||
}
|
||||
mg_destroy_server(&server);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
To compile it, put `mongoose.c`, `mongoose.h` and `app.c` into one
|
||||
folder, start terminal on UNIX or Visual Studio command line prompt on Windows,
|
||||
and run the following command:
|
||||
|
||||
cc app.c mongoose.c -pthread -o app # on Unix
|
||||
cl.exe app.c mongoose.c /TC /MD # on Windows
|
||||
|
||||
When run, this simple application opens port 8080 and serves static files,
|
||||
CGI files and lists directory content in the current working directory.
|
||||
|
||||
It is possible to generate HTML page content. Mongoose can call user-defined
|
||||
function when certain events occur.
|
||||
That function is called _an event handler_, and it is the second parameter
|
||||
to `mg_create_server()` function. Here is the example event handler function:
|
||||
|
||||
int event_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||
switch (ev) {
|
||||
case MG_AUTH: return MG_TRUE;
|
||||
default: return MG_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
Event handler is called by Mongoose with `struct mg_connection *`
|
||||
pointer and an event number. `struct mg_connection *conn`
|
||||
has all information about the request: HTTP headers, POST or websocket
|
||||
data buffer, etcetera. `enum mg_event ev` tells which exactly event is sent.
|
||||
For each event, an event handler returns a value which tells Mongoose how
|
||||
to behave.
|
||||
|
||||
The sequence of events for every connection is this:
|
||||
|
||||
* `MG_AUTH` - Mongoose asks whether this connection is authorized. If event
|
||||
handler returns `MG_FALSE`, then Mongoose does not serve the request but
|
||||
sends authorization request to the client. If `MG_TRUE` is returned,
|
||||
then Mongoose continues on with the request.
|
||||
* `MG_REQUEST` - Mongoose asks event handler to serve the request. If
|
||||
event handler serves the request by sending a reply,
|
||||
it should return `MG_TRUE`. Otherwise,
|
||||
it should return `MG_FALSE` which tells Mongoose that request is not
|
||||
served and Mongoose should serve it. For example, event handler might
|
||||
choose to serve only RESTful API requests with URIs that start with
|
||||
certain prefix, and let Mongoose serve all static files.
|
||||
If event handler decides to serve the request, but doesn't have
|
||||
all the data at the moment, it should return `MG_MORE`. That tells
|
||||
Mongoose to keep the connection open after callback returns.
|
||||
|
||||
`mg_connection::connection_param` pointer is a placeholder to keep
|
||||
user-specific data. For example, handler could decide to open a DB
|
||||
connection and store DB connection handle in `connection_param`.
|
||||
* `MG_POLL` is sent to every connection on every iteration of
|
||||
`mg_poll_server()`. Event handler should return `MG_FALSE` to ignore
|
||||
this event. If event handler returns `MG_TRUE`, then Mongoose assumes
|
||||
that event handler has finished sending data, and Mongoose will
|
||||
close the connection.
|
||||
* `MG_HTTP_ERROR` sent when Mongoose is about to send HTTP error back
|
||||
to the client. Event handler can choose to send a reply itself, in which
|
||||
case event handler must return `MG_TRUE`. Otherwise, event handler must
|
||||
return `MG_FALSE`
|
||||
* `MG_CLOSE` is sent when the connection is closed. This event is used
|
||||
to cleanup per-connection state stored in `connection_param`
|
||||
if it was allocated.
|
||||
|
||||
Let's extend our minimal application example and
|
||||
create an URI that will be served by user's C code. The app will handle
|
||||
`/hello` URI by showing a hello message. So, when app is run,
|
||||
http://127.0.0.1:8080/hello will say hello, and here's the code:
|
||||
|
||||
#include <string.h>
|
||||
#include "mongoose.h"
|
||||
|
||||
static int event_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||
if (ev == MG_AUTH) {
|
||||
return MG_TRUE; // Authorize all requests
|
||||
} else if (ev == MG_REQUEST && !strcmp(conn->uri, "/hello")) {
|
||||
mg_printf_data(conn, "%s", "Hello world");
|
||||
return MG_TRUE; // Mark as processed
|
||||
} else {
|
||||
return MG_FALSE; // Rest of the events are not processed
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_server *server = mg_create_server(NULL, event_handler);
|
||||
mg_set_option(server, "document_root", ".");
|
||||
mg_set_option(server, "listening_port", "8080");
|
||||
|
||||
for (;;) {
|
||||
mg_poll_server(server, 1000); // Infinite loop, Ctrl-C to stop
|
||||
}
|
||||
mg_destroy_server(&server);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
## Example code
|
||||
|
||||
Mongoose source code contains number of examples, located in the
|
||||
[examples](https://github.com/cesanta/mongoose/blob/master/examples/) directory.
|
||||
To build any example, go to the respective directory and run `make`.
|
||||
|
||||
## Compilation flags
|
||||
|
||||
Below is the list of compilation flags that enable or disable certain
|
||||
features. By default, some features are enabled, and could be disabled
|
||||
by setting appropriate `NO_*` flag. Features that are disabled by default
|
||||
could be enabled by setting appropriate `USE_*` flag. Bare bones Mongoose
|
||||
is quite small, about 30 kilobytes of compiled x86 code. Each feature adds
|
||||
a couple of kilobytes to the executable size, and also has some runtime penalty.
|
||||
|
||||
Note that some flags start with `NS_` prefix. This is because Mongoose uses
|
||||
[Net Skeleton](http://github.com/cesanta/net_skeleton) as a low-level
|
||||
networking engine. If user code has `#include <net_skeleton.h>`, then
|
||||
all Net Skeleton functions will be available too.
|
||||
|
||||
|
||||
-DMONGOOSE_NO_AUTH Disable MD5 authorization support
|
||||
-DMONGOOSE_NO_CGI Disable CGI support
|
||||
-DMONGOOSE_NO_DAV Disable WebDAV support
|
||||
(PUT, DELETE, MKCOL, PROPFIND methods)
|
||||
-DMONGOOSE_NO_DIRECTORY_LISTING Disable directory listing
|
||||
-DMONGOOSE_NO_FILESYSTEM Disables all file IO, serving from memory only
|
||||
-DMONGOOSE_NO_LOGGING Disable access/error logging
|
||||
-DMONGOOSE_ENABLE_THREADS Enable mg_start_thread() function
|
||||
-DMONGOOSE_NO_WEBSOCKET Disable WebSocket support
|
||||
-DMONGOOSE_NO_USER No concept of a user on used platform.
|
||||
(Platform does not provide getpwnam, setgid or setuid)
|
||||
|
||||
-DMONGOOSE_USE_IDLE_TIMEOUT_SECONDS=X Idle connection timeout, default is 30
|
||||
-DMONGOOSE_USE_LUA Enable Lua scripting
|
||||
-DMONGOOSE_USE_LUA_SQLITE3 Enable sqlite3 binding for Lua
|
||||
-DMONGOOSE_USE_POST_SIZE_LIMIT=X POST requests larger than X will be
|
||||
rejected, not set by default
|
||||
-DMONGOOSE_USE_EXTRA_HTTP_HEADERS=X Append X to the HTTP headers
|
||||
for static files, empty by default
|
||||
|
||||
-DNS_ENABLE_DEBUG Enables debug messages on stdout, very noisy
|
||||
-DNS_ENABLE_SSL Enable SSL
|
||||
-DNS_ENABLE_IPV6 Enable IPv6 support
|
||||
-DNS_ENABLE_HEXDUMP Enables hexdump of sent and received traffic
|
||||
-DNS_STACK_SIZE=X Sets stack size to X for ns_start_thread()
|
||||
-DNS_DISABLE_THREADS Disable threads support
|
||||
-DNS_DISABLE_SOCKETPAIR For systems without loopback interface
|
||||
-DMONGOOSE_SEND_NS_EVENTS Send Net Skeleton events to the event handler
|
||||
in addition to the Mongoose events
|
@ -1,45 +0,0 @@
|
||||
# Mongoose FAQ
|
||||
|
||||
## My Antivirus Software reports Mongoose as a security threat
|
||||
|
||||
Mongoose doesn't contain any malicious logic. Antivirus reports a
|
||||
[false positive](http://en.wikipedia.org/wiki/Type_I_and_type_II_errors#False_positive_error).
|
||||
This is when certain byte sequence in Mongoose accidentally matches
|
||||
virus signature in the Antivirus database.
|
||||
|
||||
## Download page doesn't work
|
||||
|
||||
Please make sure Javascript is enabled in your browser, and that the
|
||||
antivirus software is not blocking the download.
|
||||
|
||||
|
||||
## MacOS message: "Mongoose.app is damaged and can’t be opened. You should move it to the Trash"
|
||||
|
||||
This happens on newer MacOS systems. The reason for the message
|
||||
is the fact Mongoose.app is not digitally signed.
|
||||
Mongoose download procedure changes the app on the fly by injecting
|
||||
user information in the binary, making any prior digital signature void.
|
||||
Open "System Preferences" -> "Security" and set "Allow apps downloaded from"
|
||||
to "Anywhere". Revert the settings once Mongoose is installed.
|
||||
|
||||
## PHP doesn't work: getting empty page, or 'File not found' error
|
||||
|
||||
The reason for that is wrong paths to the interpreter. Remember that with PHP,
|
||||
correct interpreter is `php-cgi.exe` (`php-cgi` on UNIX). Solution: specify
|
||||
full path to the PHP interpreter, e.g.:
|
||||
|
||||
mongoose -cgi_interpreter /full/path/to/php-cgi
|
||||
|
||||
## Mongoose fails to start
|
||||
|
||||
If Mongoose exits immediately when run, this
|
||||
usually indicates a syntax error in the configuration file
|
||||
(named `mongoose.conf` by default) or the command-line arguments.
|
||||
Syntax checking is omitted from Mongoose to keep its size low. However,
|
||||
the Manual should be of help. Note: the syntax changes from time to time,
|
||||
so updating the config file might be necessary after executable update.
|
||||
|
||||
### Embedding with OpenSSL on Windows might fail because of calling convention
|
||||
|
||||
To force Mongoose to use `__stdcall` convention, add `/Gz` compilation
|
||||
flag to the Visual Studio project settings.
|
@ -1,18 +0,0 @@
|
||||
How To Share Files With Mongoose
|
||||
===========================================
|
||||
|
||||
## 1. Download Mongoose executable from http://cesanta.com/mongoose.shtml and copy the executable inside the directory you want to share:
|
||||
|
||||
![screenshot](http://cesanta.com/images/tut_sharing/tut1.png)
|
||||
|
||||
## 2. Double-click mongoose executable. A browser will start automatically, an icon will appear on a system tray in the bottom right corner of the desktop:
|
||||
|
||||
![screenshot](http://cesanta.com/images/tut_sharing/tut2.png)
|
||||
|
||||
## 3. Click on the mongoose icon
|
||||
![screenshot](http://cesanta.com/images/tut_sharing/tut3.png)
|
||||
|
||||
|
||||
## 4. Click on "Go to my address" to launch a browser locally. Or, to access a folder from another machine, launch a browser and type in the URL:
|
||||
|
||||
![screenshot](http://cesanta.com/images/tut_sharing/tut4.png)
|
@ -1,44 +0,0 @@
|
||||
# Mongoose Internals
|
||||
|
||||
Mongoose has single-threaded, event-driven, asynchronous, non-blocking core.
|
||||
`mg_create_server()` creates a web server instance. An instance is a container
|
||||
for the config options and list of active connections. To do the actual
|
||||
serving, user must call `mg_poll_server()`, which iterates over all
|
||||
active connections, performing `select()` syscall on all sockets with a
|
||||
timeout of specified number of milliseconds. When `select()` returns, Mongoose
|
||||
does an IO for each socket that has data to be sent or received. Application
|
||||
code must call `mg_poll_server()` in a loop.
|
||||
|
||||
Mongoose server instance is designed to be used by a single thread.
|
||||
It is an error to have more then
|
||||
one thread calling `mg_poll_server()`, `mg_set_option()` or any other function
|
||||
that take `struct mg_server *` parameter. Mongoose does not
|
||||
mutex-protect `struct mg_server *`, therefore the best practice is
|
||||
to call server management functions from the same thread (an IO thread).
|
||||
On a multi-core systems, many server instances can be created, sharing the
|
||||
same listening socket and managed by separate threads (see [multi_threaded.c](https://github.com/cesanta/mongoose/blob/master/examples/multi_threaded.c))
|
||||
example.
|
||||
|
||||
It is an error to pass and store `struct mg_connection *` pointers for
|
||||
later use to send data. The reason is that they can be invalidated by the
|
||||
next `mg_poll_server()` call. For such a task,
|
||||
there is `mg_iterate_over_connections()` API
|
||||
exists, which sends a callback function to the IO thread, then IO thread
|
||||
calls specified function for all active connection.
|
||||
|
||||
When mongoose buffers in HTTP request and successfully parses it, it calls
|
||||
appropriate URI handler immediately for GET requests. For POST requests,
|
||||
Mongoose delays the call until the whole POST request is buffered in memory.
|
||||
POST data is available to the callback as `struct mg_connection::content`,
|
||||
and POST data length is in `struct mg_connection::content_len`.
|
||||
|
||||
Note that websocket connections are treated the same way. Mongoose buffers
|
||||
websocket frame in memory, and calls URI handler when frame is fully
|
||||
buffered. Frame data is available `struct mg_connection::content`, and
|
||||
data length is in `struct mg_connection::content_len`, i.e. very similar to
|
||||
the POST request. `struct mg_connection::is_websocket` flag indicates
|
||||
whether the request is websocket or not. Also, for websocket requests,
|
||||
there is `struct mg_connection::wsbits` field which contains first byte
|
||||
of the websocket frame which URI handler can examine. Note that to
|
||||
reply to the websocket client, `mg_websocket_write()` should be used.
|
||||
To reply to the plain HTTP client, `mg_write()` should be used.
|
@ -1,154 +0,0 @@
|
||||
# Mongoose Configuration Options
|
||||
|
||||
### access\_control\_list
|
||||
An Access Control List (ACL) allows restrictions to be put on the list of IP
|
||||
addresses which have access to the web server. In the case of the Mongoose
|
||||
web server, the ACL is a comma separated list of IP subnets, where each
|
||||
subnet is prepended by either a `-` or a `+` sign. A plus sign means allow,
|
||||
where a minus sign means deny. If a subnet mask is omitted, such as `-1.2.3.4`,
|
||||
this means to deny only that single IP address.
|
||||
|
||||
Subnet masks may vary from 0 to 32, inclusive. The default setting is to allow
|
||||
all accesses. On each request the full list is traversed, and
|
||||
the last match wins. Example: `$ mongoose -access_control_list -0.0.0.0/0,+192.168/16` to deny all acccesses except those from `192.168/16` subnet. Note that if the option is set, then all accesses are forbidden
|
||||
by default. Thus in a previous example, `-0.0.0.0` part is not necessary.
|
||||
For example, `$mongoose access_control_list +10.0.0.0/8`
|
||||
means disallow all, allow subnet 10/8 only.
|
||||
|
||||
To learn more about subnet masks, see the
|
||||
[Wikipedia page on Subnetwork](http://en.wikipedia.org/wiki/Subnetwork)
|
||||
|
||||
Default: not set, all accesses are allowed.
|
||||
|
||||
### access\_log\_file
|
||||
Path to a file for access logs. Either full path, or relative to the
|
||||
mongoose executable. Default: not set, no query logging is done.
|
||||
|
||||
### auth_domain
|
||||
Authorization realm used in `.htpasswd` authorization. Default: `mydomain.com`
|
||||
|
||||
### cgi_interpreter
|
||||
Path to an executable to be used use as an interpreter for __all__ CGI scripts
|
||||
regardless script extension. Default: not set, Mongoose looks at
|
||||
[shebang line](http://en.wikipedia.org/wiki/Shebang_(Unix\).
|
||||
|
||||
For example, if both PHP and perl CGIs are used, then
|
||||
`#!/path/to/php-cgi.exe` and `#!/path/to/perl.exe` must be first lines of the
|
||||
respective CGI scripts. Note that paths should be either full file paths,
|
||||
or file paths relative to the directory where mongoose executable is located.
|
||||
|
||||
If all CGIs use the same interpreter, for example they are all PHP, then
|
||||
`cgi_interpreter` option can be set to the path to `php-cgi.exe` executable and
|
||||
shebang line in the CGI scripts can be omitted.
|
||||
**Note**: PHP scripts must use `php-cgi.exe`, not `php.exe`.
|
||||
|
||||
### cgi_pattern
|
||||
All files that match `cgi_pattern` are treated as CGI files. Default pattern
|
||||
allows CGI files be anywhere. To restrict CGIs to a certain directory,
|
||||
use `/path/to/cgi-bin/**.cgi` as a pattern. Note that **full file path** is
|
||||
matched against the pattern, not the URI.
|
||||
|
||||
When Mongoose starts CGI program, it creates new environment for it (in
|
||||
contrast, usually child program inherits the environment from parent). Several
|
||||
environment variables however are inherited from Mongoose's environment,
|
||||
they are: `PATH`, `TMP`, `TEMP`, `TMPDIR`, `PERLLIB`, `MONGOOSE_CGI`. On UNIX
|
||||
it is also `LD_LIBRARY_PATH`. On Windows it is also `COMSPEC`, `SYSTEMROOT`,
|
||||
`SystemDrive`, `ProgramFiles`, `ProgramFiles(x86)`, `CommonProgramFiles(x86)`.
|
||||
|
||||
Default: `**.cgi$|**.pl$|**.php$`
|
||||
|
||||
### dav\_auth\_file
|
||||
Authentication file for WebDAV mutation requests: `PUT`, `DELETE`, `MKCOL`.
|
||||
The format of that file is the same as for the `.htpasswd` file
|
||||
used for digest authentication. It can be created and managed by
|
||||
`mongoose -A` command. Default: not set, WebDAV mutations are disallowed.
|
||||
|
||||
### document_root
|
||||
A directory to serve. Default: current working directory.
|
||||
|
||||
### enable\_directory\_listing
|
||||
Enable directory listing, either `yes` or `no`. Default: `yes`.
|
||||
|
||||
### enable\_proxy
|
||||
Enable proxy functionality, either `yes` or `no`. If set to `yes`, then
|
||||
browsers can be configured to use Mongoose as a proxy. Default: `no`.
|
||||
|
||||
|
||||
### extra\_mime\_types
|
||||
Extra mime types to recognize, in form `extension1=type1,extension2=type2,...`.
|
||||
Extension must include dot. Example:
|
||||
`mongoose -extra_mime_types .cpp=plain/text,.java=plain/text`. Default: not set.
|
||||
|
||||
|
||||
### global\_auth\_file
|
||||
Path to a global passwords file, either full path or relative to the mongoose
|
||||
executable. If set, per-directory `.htpasswd` files are ignored,
|
||||
and all requests are authorised against that file. Use `mongoose -A` to
|
||||
manage passwords, or third party utilities like
|
||||
[htpasswd-generator](http://www.askapache.com/online-tools/htpasswd-generator).
|
||||
Default: not set, per-directory `.htpasswd` files are respected.
|
||||
|
||||
### hide\_files\_patterns
|
||||
A pattern for the files to hide. Files that match the pattern will not
|
||||
show up in directory listing and return `404 Not Found` if requested. Pattern
|
||||
must be for a file name only, not including directory name, e.g.
|
||||
`mongoose -hide_files_patterns secret.txt|even_more_secret.txt`. Default:
|
||||
not set.
|
||||
|
||||
### index_files
|
||||
Comma-separated list of files to be treated as directory index
|
||||
files. Default: `index.html,index.htm,index.cgi,index.shtml,index.php`
|
||||
|
||||
### listening_port
|
||||
Port to listen on. Port could be prepended by the specific IP address to bind
|
||||
to, e.g. `mongoose -listening_port 127.0.0.1:8080`. Otherwise Mongoose
|
||||
will bind to all addresses. To enable SSL, build Mongoose with
|
||||
`-DNS_ENABLE_SSL` compilation option, and specify `listening_port` as
|
||||
`ssl://PORT:SSL_CERTIFICATE.PEM`. Example SSL listener:
|
||||
`mongoose -listening_port ssl://8043:ssl_cert.pem`. Note that PEM file should
|
||||
be in PEM format, and must have both certificate and private key in it,
|
||||
concatenated together. More than one listening port can be specified,
|
||||
separated by comma,
|
||||
for example `mongoose -listening_port 8080,8000`. Default: 8080.
|
||||
|
||||
### run\_as\_user
|
||||
Switch to given user credentials after startup. UNIX-only. This option is
|
||||
required when mongoose needs to bind on privileged port on UNIX, e.g.
|
||||
|
||||
$ sudo mongoose -listening_port 80 -run_as_user nobody
|
||||
|
||||
Default: not set.
|
||||
|
||||
### url\_rewrites
|
||||
Comma-separated list of URL rewrites in the form of
|
||||
`uri_pattern=file_or_directory_path`. When Mongoose receives the request,
|
||||
it constructs the file name to show by combining `document_root` and the URI.
|
||||
However, if the rewrite option is used and `uri_pattern` matches the
|
||||
requested URI, then `document_root` is ignored. Instead,
|
||||
`file_or_directory_path` is used, which should be a full path name or
|
||||
a path relative to the web server's current working directory. Note that
|
||||
`uri_pattern`, as all mongoose patterns, is a prefix pattern. If `uri_pattern`
|
||||
is a number, then it is treated as HTTP error code, and `file_or_directory_path`
|
||||
should be an URI to redirect to. Mongoose will issue `302` temporary redirect
|
||||
to the specified URI with following parameters:
|
||||
`?code=HTTP_ERROR_CODE&orig_uri=ORIGINAL_URI&query_string=QUERY_STRING`.
|
||||
|
||||
If `uri_pattern` starts with `@` symbol, then Mongoose compares
|
||||
it with the `HOST` header of the request. If they are equal, Mongoose sets
|
||||
document root to `file_or_directory_path`, implementing virtual hosts support.
|
||||
|
||||
Examples:
|
||||
|
||||
# Redirect all accesses to `.doc` files to a special script
|
||||
mongoose -url_rewrites **.doc$=/path/to/cgi-bin/handle_doc.cgi
|
||||
|
||||
# Implement user home directories support
|
||||
mongoose -url_rewrites /~joe/=/home/joe/,/~bill=/home/bill/
|
||||
|
||||
# Redirect 404 errors to a specific error page
|
||||
mongoose -url_rewrites 404=/cgi-bin/error.cgi
|
||||
|
||||
# Virtual hosts example: serve foo.com domain from different directory
|
||||
mongoose -url_rewrites @foo.com=/var/www/foo.com
|
||||
|
||||
Default: not set.
|
@ -1,49 +0,0 @@
|
||||
How To Create A PHP Website With Mongoose
|
||||
===========================================
|
||||
|
||||
## 1. Create a directory which will contain your website files. For example, on drive `C:\`, create a directory called `my_website`:
|
||||
|
||||
![screenshot](http://cesanta.com/images/tut_php/tut1.png)
|
||||
|
||||
## 2. Inside `my_website` directory, create a new file called "index". This will be the default web page shown when the website is visited.
|
||||
|
||||
![screenshot](http://cesanta.com/images/tut_php/tut2.png)
|
||||
|
||||
## 3. Open index file with your favorite editor (for example, Notepad) and enter some HTML / PHP code:
|
||||
|
||||
![screenshot](http://cesanta.com/images/tut_php/tut3.png)
|
||||
|
||||
## 4. Save this file as `index.php`:
|
||||
|
||||
![screenshot](http://cesanta.com/images/tut_php/tut4.png)
|
||||
|
||||
|
||||
## 5. Download Mongoose executable from http://cesanta.com/mongoose.shtml and copy the executable inside `my_website` directory:
|
||||
|
||||
![screenshot](http://cesanta.com/images/tut_php/tut5.png)
|
||||
|
||||
## 6. Double-click mongoose executable. An icon will appear on a system tray in the bottom right corner of the desktop:
|
||||
|
||||
![screenshot](http://cesanta.com/images/tut_php/tut6.png)
|
||||
|
||||
## 7. Download PHP 5.3 zip (do NOT download PHP 5.5 cause you might have missing DLLs problem) from http://windows.php.net/download and extract it to `C:\php5` directory:
|
||||
![screenshot](http://cesanta.com/images/tut_php/tut7.png)
|
||||
|
||||
## 8. Click on the mongoose icon and choose "Edit Settings" menu.:
|
||||
![screenshot](http://cesanta.com/images/tut_php/tut8.png)
|
||||
|
||||
## 9. A settings dialog will appear. Click on `cgi_interpreter` button:
|
||||
|
||||
![screenshot](http://cesanta.com/images/tut_php/tut9.png)
|
||||
|
||||
## 10. Choose `C:\php5\php-cgi.exe` and click "Save Settings":
|
||||
|
||||
![screenshot](http://cesanta.com/images/tut_php/tut10.png)
|
||||
|
||||
## 11. Click on the mongoose icon and choose "Go to my address" menu:
|
||||
![screenshot](http://cesanta.com/images/tut_php/tut11.png)
|
||||
|
||||
|
||||
## 12. A browser will popup displaying `index.php`.
|
||||
|
||||
![screenshot](http://cesanta.com/images/tut_php/tut12.png)
|
@ -1,218 +0,0 @@
|
||||
# Mongoose Release Notes
|
||||
|
||||
## Release 5.6, 2015-03-17
|
||||
|
||||
Changes in Libmongoose library:
|
||||
|
||||
- Added `-dav_root` configuration option that gives an ability to mount
|
||||
a different root directory (not document_root)
|
||||
- Fixes for build under Win23 and MinGW
|
||||
- Bugfix: Double dots removal
|
||||
- Bugfix: final chunked response double-send
|
||||
- Fixed compilation in 64-bit environments
|
||||
- Added OS/2 compatibility
|
||||
- Added `getaddrinfo()` call and `NS_ENABLE_GETADDRINFO`
|
||||
- Various SSL-related fixes
|
||||
- Added integer overflow protection in `iobuf_append()` and `deliver_websocket_frame()`
|
||||
- Fixed NetBSD build
|
||||
- Enabled `NS_ENABLE_IPV6` build for Visual Studio 2008+
|
||||
- Enhanced comma detection in `parse_header()`
|
||||
- Fixed unchanged memory accesses on ARM
|
||||
- Added ability to use custom memory allocator through NS_MALLOC, NS_FREE, NS_REALLOC
|
||||
|
||||
Changes in Mongoose binary:
|
||||
|
||||
- Added `-start_browser` option to disable automatic browser launch
|
||||
- Added experimental SSL support. To listen on HTTPS port, use `ssl://PORT:SSL_CERT` format. For example, to listen on HTTP port 8080 and HTTPS port 8043, use `-listening_port 8080,ssl://8043:ssl_cert.pem`
|
||||
|
||||
## Release 5.5, October 28 2014
|
||||
|
||||
Changes in Libmongoose library:
|
||||
|
||||
- Added new API function: `mg_forward()` for proxying functionality
|
||||
- Added new API function: `mg_send_file_data()` for sending file data
|
||||
- Added new utility API functions: `mg_mmap() and mg_munmap()`
|
||||
- Changed the way SSL settings are handled: removed `ssl_certificate` and
|
||||
`ssl_ca_certificate` options, and instead made `listening_port` accept
|
||||
`ssl://PORT:SSL_CERT:CA_CERT` notation
|
||||
- Added ability to listen on multiple ports, see `listening_port` documentation
|
||||
- Added `enable_proxy` option
|
||||
- Added [cookie_authentication](https://github.com/cesanta/mongoose/tree/master/examples/cookie_authentication) example
|
||||
- Added [websocket\_ssl\_proxy](https://github.com/cesanta/mongoose/tree/master/examples/websocket_ssl_proxy) example
|
||||
- Added [http_client](https://github.com/cesanta/mongoose/tree/master/examples/http_client) example
|
||||
- Increased default 'idle connection' timeout from 30 to 300 seconds
|
||||
- Fixed MinGW build
|
||||
- Refactored all examples, put each in it's own directory with dedicated build
|
||||
- Many smaller bugfixed, including SSL, CGI, API, proxy, etc
|
||||
|
||||
Changes in pre-compiled binaries:
|
||||
|
||||
- Support for multiple listening ports
|
||||
- Fixed CGI handling for scripts that specify interpreter in the hashbang line
|
||||
|
||||
## Release 5.4, July 28 2014
|
||||
|
||||
Changes in Libmongoose library:
|
||||
|
||||
- Added `hexdump_file` option for low-level request/reply debugging
|
||||
- Added `mg_template()` API function for generating HTML pages from
|
||||
templates with expansions
|
||||
- Fixed `struct mg_connection::local_ip` handling, `mg_set_option()`
|
||||
behavior with NULL values
|
||||
- Added `mg_send_file()` call to send arbitrary file to the client
|
||||
- Added `mg_terminate_ssl()` for SSL termination functionality
|
||||
- Added HTTP proxy support, `enable_proxy` config option
|
||||
- Added `mg_next()` for iterating over existing active connections
|
||||
- Added client-side SSL auth, `ssl_ca_certificate` option
|
||||
- Added `mg_wakeup_server_ex()` for pushing messages to existing connections
|
||||
- Added `MG_WS_HANDSHAKE` and `MG_WS_CONNECT` events that are sent on
|
||||
Websocket handshake is connection establishment, respectively
|
||||
- Removed server-side Lua support
|
||||
- Filesystem access, reading from socket/SSL performance improvements
|
||||
- DAV PROPFIND memory leak fixed
|
||||
- Added `big_upload.c` and enhanced `upload.c` example
|
||||
- Added `proxy.c` example that demonstrates proxy functionality and SSE pushes
|
||||
- Added `websocket2.c` example that shows simple web chat implementation
|
||||
over websockets
|
||||
- Various minor fixes
|
||||
|
||||
|
||||
Changes in pre-compiled binaries:
|
||||
|
||||
- Created HTML administration console
|
||||
- When server is started, browser is started automatically
|
||||
- Fixed directory listing bug when directory contains `#` character
|
||||
- Removed built-in Lua Server Pages in the binary, and instead
|
||||
added Mongoose + Lua developer bundle which has Lua Server Pages support.
|
||||
That also solves external Lua modules loading problem.
|
||||
|
||||
|
||||
## Release 5.3, March 10 2014
|
||||
|
||||
Changes in Libmongoose library:
|
||||
|
||||
* Moved to the evented API. Updated API documentation is at
|
||||
http://cesanta.com/docs/Embed.shtml
|
||||
http://cesanta.com/docs/API.shtml
|
||||
* Added `MG_LUA` event for exporting custom variables to the Lua environment
|
||||
* Added virtual hosts capability, see `url_rewrites` option description at
|
||||
http://cesanta.com/docs/Options.shtml
|
||||
* Added mjpg serving example
|
||||
* Cleaned up and documented HTTP client API, with unit tests
|
||||
* Added `mg_wakeup_server()` to awaken `mg_poll_server()`
|
||||
from another thread
|
||||
* Moved Mongoose IO core to [https://github.com/cesanta/net_skeleton](Net Skeleton)
|
||||
* Added connection hexdump functionality for developers
|
||||
* Bug fixes
|
||||
|
||||
Changes in pre-compiled binaries:
|
||||
|
||||
* New awesome Mongoose logos by our designer Katrin - thanks Katrin!
|
||||
Check them out at http://cesanta.com/products.shtml
|
||||
* Added Lua Server Pages support to the free version, quick intro is at
|
||||
http://cesanta.com/docs/Lua.shtml
|
||||
* Added quick "Set shared directory" menu item to set `document_root`
|
||||
* Added SSI support to the Pro version
|
||||
* Removed SSL support from the Pro version
|
||||
|
||||
## Release 5.2, Feb 1 2014
|
||||
|
||||
* Windows binary made fully UNICODE aware. In previous versions,
|
||||
the presence of non-ASCII chars in document root, CGI script name,
|
||||
or directory name might have broken Mongoose as stand-alone
|
||||
or as Windows service. Now Mongoose works with non-ASCII paths properly.
|
||||
Internally, Mongoose uses UTF8 encoding. When making WinAPI calls,
|
||||
mongoose converts UTF8 strings to wide chars and calls UNICODE API.
|
||||
* Enhanced authorization API by providing `mg_set_auth_handler()` and
|
||||
`mg_authorize_digest()`
|
||||
* Removed `mg_add_uri_handler()`, added `mg_set_request_handler()`.
|
||||
There is only oneURI handler that handles all requests, just like in 4.x.
|
||||
The reason for this change is to provide an ability to catch all URIs,
|
||||
and at the same time signal Mongoose to continue handling specific URIs.
|
||||
* Added `mg_parse_multipart()` API for file uploads.
|
||||
Note that the restriction on uploading huge files still exists,
|
||||
and will be eliminated in the next release.
|
||||
* Allowing mongoose to bind to port 0, in which case it'll bind to any
|
||||
random unused port.
|
||||
* Moved `idle_timeout_ms` run-time option to compile-time flag
|
||||
* Added asynchronous HTTP client, not documented yet. Documentation and
|
||||
examples are coming in the next couple of weeks. Async Websocket client
|
||||
is scheduled for the next release. See usage examples at `unit_test.c`
|
||||
* Windows and MacOS pre-built binaries are now split to free and paid ones,
|
||||
paid binaries include CGI, SSL, Lua, Sqlite, support and updates.
|
||||
Linux pre-built binary includes all functionality and is free, and will
|
||||
continue to be free. Source code for Windows and MacOS GUI is closed.
|
||||
Disclaimer: source code for the command line stand-alone server,
|
||||
as well as Mongoose library itself, will never be closed.
|
||||
* Multiple bug fixes and minor enhancements
|
||||
|
||||
## Release 5.1, Jan 10 2014
|
||||
|
||||
* CGI-related bugs where fixed, primarily for Windows platform
|
||||
* Bugs on Windows related to UNICODE support were fixed
|
||||
* Added a feature to support "error pages" through redirect.
|
||||
Done using `-url_redirects` option, details are on
|
||||
http://cesanta.com/docs/Options.shtml
|
||||
|
||||
## Release 5.0, Jan 6 2014
|
||||
|
||||
* Internal core has been changed from blocking, thread-per-connection to
|
||||
non-blocking, asynchronous, one thread for all.
|
||||
* API modification for server creation and response creation. That allowed
|
||||
keep-alive support for dynamic requests, boosting the embedded performance
|
||||
to 100+ thousands requests per second on a single core
|
||||
(as measured on my development MacBook laptop)
|
||||
* Unified handling of POST requests and Websocket requests by putting a
|
||||
payload into `conn->content`, `conn->content_len` attributes.
|
||||
That simplified user code and eliminated the need of `mg_read()`,
|
||||
since mongoose buffers all data prior to calling the callback
|
||||
* keep-alive support is the default
|
||||
* Dropped SSI support and throttling support
|
||||
* Several configuration parameters are gone:
|
||||
* `cgi_environment` (replaced with MONGOOSE_CGI),
|
||||
* `protect_uri` (not useful)
|
||||
* `ssi_pattern` (SSI support is gone)
|
||||
* `throttle` (throttling support is gone)
|
||||
* `error_log_file` (not used)
|
||||
* `enable_keep_alive` (enabled by default)
|
||||
* `listening_ports` (renamed to listening_port)
|
||||
* `num_threads` (core has changed to single thread)
|
||||
* `put_delete_auth_file` (renamed to dav_auth_file)
|
||||
* `authentication_domain` (renamed to auth_domain)
|
||||
* Due to the async, non-blocking nature of the core, few restrictions
|
||||
are now in place:
|
||||
* user callbacks must not block
|
||||
* POST and Websocket data are now buffered, and cannot be huge
|
||||
* mongoose is now capable on listening on only one port
|
||||
|
||||
## Release 4.1, Oct 2013
|
||||
## Release 4.0, Oct 2013
|
||||
## Release 3.8, Sep 2013
|
||||
|
||||
## Release 3.7, Feb 2 2013
|
||||
|
||||
* Added "redirect to SSL port" functionality, e.g. if you specify
|
||||
`-listening_ports 8080r,8043s`
|
||||
then all requests to HTTP port 8080 will be redirected to HTTPS port 8043
|
||||
* Added `mg_download()` API, an HTTP client interface!
|
||||
* Lua server pages now must output HTTP headers -- full control for Lua
|
||||
* Added pre-built binary for MacOS, with initial GUI support
|
||||
* API change: got rid of events, moved to struct `mg_callbacks`
|
||||
* Bugfixes, thanks to contributors
|
||||
|
||||
|
||||
## Release 3.7, Jan 18 2013
|
||||
* Fixed source code archive (main.c was missing)
|
||||
* Extended Windows GUI functionality:
|
||||
* Added "Start browser" systray popup menu item
|
||||
* Enhanced configuration editor
|
||||
* Renamed config options:
|
||||
* `put_delete_passwords_file` -> `put_delete_auth_file`
|
||||
* `global_passwords_file` -> `global_auth_file`
|
||||
* `select()` changed to `poll()`, to avoid big file descriptor
|
||||
`FD_SET` problem on UNIX
|
||||
* Couple of bugfixes, thanks to contributors
|
||||
|
||||
|
||||
Earlier release notes could be found by searching
|
||||
[Mongoose mailing list](https://groups.google.com/forum/#!forum/mongoose-users)
|
@ -1,70 +0,0 @@
|
||||
# Mongoose SSL guide
|
||||
|
||||
SSL is a protocol that makes web communication secure. To enable SSL
|
||||
in mongoose, 2 steps are required:
|
||||
|
||||
1. Create valid SSL certificate file
|
||||
2. Append SSL certificate file path to the `listening_ports` option
|
||||
|
||||
Below is the `mongoose.conf` file snippet for typical SSL setup:
|
||||
|
||||
document_root www_root # Serve files in www_root directory
|
||||
listening_ports 80,443:cert.pem # Listen on ports 80 and 443
|
||||
|
||||
## How to create SSL certificate file
|
||||
|
||||
SSL certificate file is a text file that must contain at least two
|
||||
sections:
|
||||
|
||||
1. A private key
|
||||
2. A certificate
|
||||
|
||||
Both sections should be chunks of text in PEM format. When PEM file is
|
||||
opened in a text editor, it looks like this:
|
||||
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEAwONaLOP7EdegqjRuQKSDXzvHmFMZfBufjhELhNjo5KsL4ieH
|
||||
hYN0Zii2yTb63jGxKY6gH1R/r9dL8kXaJmcZrfSa3AgywnteJWg=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDBjCCAe4CCQCX05m0b053QzANBgkqhkiG9w0BAQQFADBFMQswCQYDVQQGEwJB
|
||||
SEGI4JSxV56lYg==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
Two aforementioned sections are clearly seen. Typically, those section
|
||||
are bigger then in the example shown. The text between the `BEGIN` and
|
||||
`END` is the text representation of binary data, a private key and a
|
||||
certificate. Therefore, in order to create a certificate file,
|
||||
|
||||
* private key must be converted to PEM format
|
||||
* certificate must be converted to PEM format
|
||||
* those two should be concatenated into a single file
|
||||
|
||||
If the certificate chain in used, a chain file also needs to be
|
||||
converted into PEM format and appended to the certificate file.
|
||||
|
||||
## How SSL works
|
||||
|
||||
SSL is a protocol that can encrypt communication between two parties. If third
|
||||
party observes all messages passed by, it would be very
|
||||
hard for the third party (though not impossible) to decrypt the communication.
|
||||
|
||||
The idea is based on so-called public key encryption. Communicating parties
|
||||
have two keys: a public key and a private key. A public key is advertised
|
||||
to everybody, and it is contained in a certificate. A private key is kept
|
||||
secret. Security algorithm works in a way that anybody can encrypt
|
||||
a message using public key, and only private key can decrypt it.
|
||||
|
||||
This is why web server needs both private key and certificate: private key
|
||||
is used to decrypt incoming messages, and certificate is used to tell the
|
||||
public key to the other party. When communication starts, parties exchange
|
||||
their public keys, and keep private keys to themselves. Man-in-the-middle
|
||||
who observes the communication is unable to decrypt the messages cause
|
||||
private keys are required for decryption.
|
||||
|
||||
Encryption algorithms are built on top of hard mathematical problem, which
|
||||
makes it very expensive for man-in-the-middle to compute private keys.
|
||||
For example, RSA algorithm is based on a mathematical problem of factorization.
|
||||
It is easy to generate two very large prime numbers `P` and `Q` and make
|
||||
a product `P * Q`. But given a product, it is very hard to recover these
|
||||
two prime numbers - this is called factorization.
|
@ -1,86 +0,0 @@
|
||||
# Mongoose User Guide
|
||||
|
||||
Mongoose is small and easy to use web server built on top of
|
||||
mongoose library. It is designed with maximum simplicity in mind. For example,
|
||||
to share any directory, just drop mongoose executable in that directory,
|
||||
double-click it (on UNIX, run it from shell) and launch a browser at
|
||||
[http://localhost:8080](http://localhost:8080) Note that 'localhost' should
|
||||
be changed to a machine's name if a folder is accessed from other computer.
|
||||
|
||||
On Windows and Mac, Mongoose iconifies itself to the system tray when started.
|
||||
Right-click on the icon to pop up a menu, where it is possible to stop
|
||||
mongoose, or configure it.
|
||||
|
||||
On UNIX, `mongoose` is a command line utility. Running `mongoose` in
|
||||
terminal, optionally followed by configuration parameters
|
||||
(`mongoose [OPTIONS]`) or configuration file name
|
||||
(`mongoose [config_file_name]`) starts the
|
||||
web server:
|
||||
|
||||
$ mongoose -document_root /var/www # Running mongoose with cmdline options
|
||||
$ mongoose /etc/my_config.txt # Running mongoose with config file
|
||||
$ mongoose # Running with no parameters. This will
|
||||
# serve current directory on port 8080
|
||||
|
||||
Mongoose does not detach from terminal. Pressing `Ctrl-C` keys
|
||||
stops the server.
|
||||
|
||||
When started, mongoose first searches for the configuration file.
|
||||
If configuration file is specified explicitly in the command line, then
|
||||
specified configuration file is used.
|
||||
Otherwise, mongoose would search for file `mongoose.conf` in the same directory
|
||||
where binary is located, and use it. Configuration file can be absent.
|
||||
|
||||
Configuration file is a sequence of lines, each line containing
|
||||
command line argument name and it's value. Empty lines and lines beginning
|
||||
with `#` are ignored. Here is the example of `mongoose.conf` file:
|
||||
|
||||
# This is a comment
|
||||
document_root C:\www
|
||||
listening_port 80
|
||||
ssl_certificate C:\mongoose\ssl_cert.pem
|
||||
|
||||
Command line arguments are highest priority and can override
|
||||
configuration file settings. For example, if `mongoose.conf` has line
|
||||
`document_root /var/www`, and mongoose has been started as
|
||||
`mongoose -document_root /etc`, then `/etc` directory will be used as
|
||||
document root.
|
||||
|
||||
Note that configuration options on the command line must start with `-`,
|
||||
and their names are the same as in the config file. Exampli gratia,
|
||||
the following two setups are equivalent:
|
||||
|
||||
$ mongoose -listening_port 1234 -document_root /var/www
|
||||
|
||||
$ cat > mongoose.conf
|
||||
listening_ports 1234
|
||||
document_root /var/www
|
||||
^D
|
||||
$ mongoose
|
||||
|
||||
Mongoose can also be used to modify `.htpasswd` passwords file:
|
||||
|
||||
$ mongoose -A .htpasswd mydomain.com user_name user_password
|
||||
|
||||
Unlike other web servers, mongoose does not require CGI scripts be located in
|
||||
a special directory. CGI scripts can be anywhere. CGI (and SSI) files are
|
||||
recognized by the file name pattern. Mongoose uses shell-like glob
|
||||
patterns. Pattern match starts at the beginning of the string, so essentially
|
||||
patterns are prefix patterns. Syntax is as follows:
|
||||
|
||||
** Matches everything
|
||||
* Matches everything but slash character, '/'
|
||||
? Matches any character
|
||||
$ Matches the end of the string
|
||||
| Matches if pattern on the left side or the right side matches.
|
||||
|
||||
All other characters in the pattern match themselves. Examples:
|
||||
|
||||
# Pattern Meaning
|
||||
**.cgi$ Any string that ends with .cgi
|
||||
/foo Any string that begins with /foo
|
||||
**a$|**b$ Any string that ends with a or b
|
||||
|
||||
To restrict CGI files only to `/cgi-bin/` directory, use this setting:
|
||||
|
||||
$ mongoose -cgi_pattern /cgi-bin/*.cgi # Emulate /cgi-bin/ restriction
|
@ -1 +0,0 @@
|
||||
*.exe
|
@ -1,20 +0,0 @@
|
||||
# Copyright (c) 2014 Cesanta Software
|
||||
# All rights reserved
|
||||
|
||||
SUBDIRS = $(sort $(filter-out csharp/, $(dir $(wildcard */))))
|
||||
X = $(SUBDIRS)
|
||||
ifdef WINDIR
|
||||
# appending the Winsock2 library at the end of the compiler
|
||||
# invocation
|
||||
CFLAGS_EXTRA += -lws2_32
|
||||
endif
|
||||
|
||||
.PHONY: $(SUBDIRS)
|
||||
|
||||
all: $(SUBDIRS)
|
||||
|
||||
$(SUBDIRS):
|
||||
@$(MAKE) CFLAGS_EXTRA="$(CFLAGS_EXTRA)" -C $@
|
||||
|
||||
clean:
|
||||
for d in $(SUBDIRS) ; do $(MAKE) -C $$d clean ; done
|
@ -1,21 +0,0 @@
|
||||
# Copyright (c) 2014 Cesanta Software
|
||||
# All rights reserved
|
||||
|
||||
PROG = array_vars
|
||||
CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
|
||||
SOURCES = $(PROG).c ../../mongoose.c
|
||||
|
||||
all: $(PROG)
|
||||
|
||||
run: $(PROG)
|
||||
./$(PROG)
|
||||
|
||||
$(PROG): $(SOURCES) Makefile
|
||||
$(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
|
||||
|
||||
win:
|
||||
wine cl $(SOURCES) /MD /nologo /DNDEBUG /O1 /I../.. /Fe$(PROG).exe
|
||||
wine $(PROG).exe
|
||||
|
||||
clean:
|
||||
rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib *.gc*
|
@ -1,45 +0,0 @@
|
||||
// Copyright (c) 2014 Cesanta Software
|
||||
// All rights reserved
|
||||
//
|
||||
// This example demostrates how to use array get variables using mg_get_n_var
|
||||
// $Date: 2014-09-09 22:20:23 UTC $
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "mongoose.h"
|
||||
|
||||
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||
switch (ev) {
|
||||
case MG_AUTH: return MG_TRUE;
|
||||
case MG_REQUEST:
|
||||
{
|
||||
mg_printf_data(conn, "Hello! Requested URI is [%s] ", conn->uri);
|
||||
char buffer[1024];
|
||||
int i, ret;
|
||||
for(i=0; (ret = mg_get_var_n(conn, "foo[]", buffer, 1024, i)) > 0; i++)
|
||||
mg_printf_data(conn, "\nfoo[%d] = %s", i, buffer);
|
||||
|
||||
return MG_TRUE;
|
||||
}
|
||||
default: return MG_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_server *server;
|
||||
|
||||
// Create and configure the server
|
||||
server = mg_create_server(NULL, ev_handler);
|
||||
mg_set_option(server, "listening_port", "8080");
|
||||
|
||||
// Serve request. Hit Ctrl-C to terminate the program
|
||||
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
|
||||
for (;;) {
|
||||
mg_poll_server(server, 1000);
|
||||
}
|
||||
|
||||
// Cleanup, and free server instance
|
||||
mg_destroy_server(&server);
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
# Copyright (c) 2014 Cesanta Software
|
||||
# All rights reserved
|
||||
|
||||
PROG = big_upload
|
||||
CFLAGS = -W -Wall -pthread -I../.. -g -O0 $(CFLAGS_EXTRA)
|
||||
SOURCES = $(PROG).c ../../mongoose.c
|
||||
|
||||
$(PROG): $(SOURCES)
|
||||
$(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
|
||||
|
||||
clean:
|
||||
rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
|
@ -1,84 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "mongoose.h"
|
||||
|
||||
static int handle_request(struct mg_connection *conn) {
|
||||
if (strcmp(conn->uri, "/upload") == 0) {
|
||||
FILE *fp = (FILE *) conn->connection_param;
|
||||
if (fp != NULL) {
|
||||
fwrite(conn->content, 1, conn->content_len, fp); // Write last bits
|
||||
mg_printf(conn, "HTTP/1.1 200 OK\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Connection: close\r\n\r\n"
|
||||
"Written %ld of POST data to a temp file:\n\n",
|
||||
(long) ftell(fp));
|
||||
|
||||
// Temp file will be destroyed after fclose(), do something with the
|
||||
// data here -- for example, parse it and extract uploaded files.
|
||||
// As an example, we just echo the whole POST buffer back to the client.
|
||||
rewind(fp);
|
||||
mg_send_file_data(conn, fileno(fp));
|
||||
return MG_MORE; // Tell Mongoose reply is not completed yet
|
||||
} else {
|
||||
mg_printf_data(conn, "%s", "Had no data to write...");
|
||||
return MG_TRUE; // Tell Mongoose we're done with this request
|
||||
}
|
||||
} else {
|
||||
mg_printf_data(conn, "%s",
|
||||
"<html><body>Upload example."
|
||||
"<form method=\"POST\" action=\"/upload\" "
|
||||
" enctype=\"multipart/form-data\">"
|
||||
"<input type=\"file\" name=\"file\" /> <br/>"
|
||||
"<input type=\"submit\" value=\"Upload\" />"
|
||||
"</form></body></html>");
|
||||
return MG_TRUE; // Tell mongoose to close this connection
|
||||
}
|
||||
}
|
||||
|
||||
// Mongoose sends MG_RECV for every received POST chunk.
|
||||
// When last POST chunk is received, Mongoose sends MG_REQUEST, then MG_CLOSE.
|
||||
static int handle_recv(struct mg_connection *conn) {
|
||||
FILE *fp = (FILE *) conn->connection_param;
|
||||
|
||||
// Open temporary file where we going to write data
|
||||
if (fp == NULL && ((conn->connection_param = fp = tmpfile())) == NULL) {
|
||||
return -1; // Close connection on error
|
||||
}
|
||||
|
||||
// Return number of bytes written to a temporary file: that is how many
|
||||
// bytes we want to discard from the receive buffer
|
||||
return fwrite(conn->content, 1, conn->content_len, fp);
|
||||
}
|
||||
|
||||
// Make sure we free all allocated resources
|
||||
static int handle_close(struct mg_connection *conn) {
|
||||
if (conn->connection_param != NULL) {
|
||||
fclose((FILE *) conn->connection_param);
|
||||
conn->connection_param = NULL;
|
||||
}
|
||||
return MG_TRUE;
|
||||
}
|
||||
|
||||
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||
switch (ev) {
|
||||
case MG_AUTH: return MG_TRUE;
|
||||
case MG_REQUEST: return handle_request(conn);
|
||||
case MG_RECV: return handle_recv(conn);
|
||||
case MG_CLOSE: return handle_close(conn);
|
||||
default: return MG_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_server *server = mg_create_server(NULL, ev_handler);
|
||||
mg_set_option(server, "listening_port", "8080");
|
||||
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
|
||||
|
||||
for (;;) {
|
||||
mg_poll_server(server, 1000);
|
||||
}
|
||||
|
||||
mg_destroy_server(&server);
|
||||
return 0;
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
# Copyright (c) 2014 Cesanta Software
|
||||
# All rights reserved
|
||||
|
||||
PROG = cookie_auth
|
||||
CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
|
||||
SOURCES = $(PROG).c ../../mongoose.c
|
||||
|
||||
$(PROG): $(SOURCES)
|
||||
$(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
|
||||
|
||||
clean:
|
||||
rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
|
@ -1,97 +0,0 @@
|
||||
// Copyright (c) 2014 Cesanta Software
|
||||
// All rights reserved
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include "mongoose.h"
|
||||
|
||||
static const char *s_login_uri = "/login.html";
|
||||
static const char *s_secret = ":-)"; // Must be known only to server
|
||||
|
||||
static void generate_ssid(const char *user_name, const char *expiration_date,
|
||||
char *ssid, size_t ssid_size) {
|
||||
char hash[33];
|
||||
mg_md5(hash, user_name, ":", expiration_date, ":", s_secret, NULL);
|
||||
snprintf(ssid, ssid_size, "%s|%s|%s", user_name, expiration_date, hash);
|
||||
}
|
||||
|
||||
static int check_auth(struct mg_connection *conn) {
|
||||
char ssid[100], calculated_ssid[100], name[100], expire[100];
|
||||
|
||||
// Always authenticate requests to login page
|
||||
if (strcmp(conn->uri, s_login_uri) == 0) {
|
||||
return MG_TRUE;
|
||||
}
|
||||
|
||||
// Look for session ID in the Cookie.
|
||||
// That session ID can be validated against the database that stores
|
||||
// current active sessions.
|
||||
mg_parse_header(mg_get_header(conn, "Cookie"), "ssid", ssid, sizeof(ssid));
|
||||
if (sscanf(ssid, "%[^|]|%[^|]|", name, expire) == 2) {
|
||||
generate_ssid(name, expire, calculated_ssid, sizeof(calculated_ssid));
|
||||
if (strcmp(ssid, calculated_ssid) == 0) {
|
||||
return MG_TRUE; // Authenticate
|
||||
}
|
||||
}
|
||||
|
||||
// Auth failed, do NOT authenticate, redirect to login page
|
||||
mg_printf(conn, "HTTP/1.1 302 Moved\r\nLocation: %s\r\n\r\n", s_login_uri);
|
||||
return MG_FALSE;
|
||||
}
|
||||
|
||||
static int check_login_form_submission(struct mg_connection *conn) {
|
||||
char name[100], password[100], ssid[100], expire[100], expire_epoch[100];
|
||||
|
||||
mg_get_var(conn, "name", name, sizeof(name));
|
||||
mg_get_var(conn, "password", password, sizeof(password));
|
||||
|
||||
// A real authentication mechanism should be employed here.
|
||||
// Also, the whole site should be served through HTTPS.
|
||||
if (strcmp(name, "Joe") == 0 && strcmp(password, "Doe") == 0) {
|
||||
// Generate expiry date
|
||||
time_t t = time(NULL) + 3600; // Valid for 1 hour
|
||||
snprintf(expire_epoch, sizeof(expire_epoch), "%lu", (unsigned long) t);
|
||||
strftime(expire, sizeof(expire), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&t));
|
||||
generate_ssid(name, expire_epoch, ssid, sizeof(ssid));
|
||||
// Set "session id" cookie, there could be some data encoded in it.
|
||||
mg_printf(conn,
|
||||
"HTTP/1.1 302 Moved\r\n"
|
||||
"Set-Cookie: ssid=%s; expire=\"%s\"; http-only; HttpOnly;\r\n"
|
||||
"Content-Length: 0\r\n"
|
||||
"Location: /\r\n\r\n",
|
||||
ssid, expire);
|
||||
return MG_TRUE;
|
||||
}
|
||||
return MG_FALSE;
|
||||
}
|
||||
|
||||
static int serve_request(struct mg_connection *conn) {
|
||||
if (strcmp(conn->uri, s_login_uri) == 0 &&
|
||||
strcmp(conn->request_method, "POST") == 0) {
|
||||
return check_login_form_submission(conn);
|
||||
}
|
||||
return MG_FALSE; // Serve files in the document_root
|
||||
}
|
||||
|
||||
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||
switch (ev) {
|
||||
case MG_AUTH: return check_auth(conn);
|
||||
case MG_REQUEST: return serve_request(conn);
|
||||
default: return MG_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_server *server = mg_create_server(NULL, ev_handler);
|
||||
mg_set_option(server, "listening_port", "8080");
|
||||
mg_set_option(server, "document_root", ".");
|
||||
|
||||
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
|
||||
for (;;) {
|
||||
mg_poll_server(server, 1000);
|
||||
}
|
||||
mg_destroy_server(&server);
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>WebSocket Test</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<style type="text/css">
|
||||
body {
|
||||
background-color: #cde; margin: 0;
|
||||
padding: 0; font: 14px Helvetica, Arial, sans-serif;
|
||||
}
|
||||
* { outline: none; }
|
||||
div.content {
|
||||
width: 800px; margin: 2em auto; padding: 20px 50px;
|
||||
background-color: #fff; border-radius: 1em;
|
||||
}
|
||||
label { display: inline-block; min-width: 7em; }
|
||||
input { border: 1px solid #ccc; padding: 0.4em; margin: 0 0 10px 0; }
|
||||
a:link, a:visited { color: #69c; text-decoration: none; }
|
||||
@media (max-width: 700px) {
|
||||
body { background-color: #fff; }
|
||||
div.content {
|
||||
width: auto; margin: 0 auto; border-radius: 0; padding: 1em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<body>
|
||||
<div class="content">
|
||||
<h1>Mongoose Cookie Base Authentication</h1>
|
||||
<p>This is an index page. Authentication succeeded.</p>
|
||||
</body>
|
||||
</html>
|
@ -1,44 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>WebSocket Test</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<style type="text/css">
|
||||
body {
|
||||
background-color: #cde; margin: 0;
|
||||
padding: 0; font: 14px Helvetica, Arial, sans-serif;
|
||||
}
|
||||
* { outline: none; }
|
||||
div.content {
|
||||
width: 800px; margin: 2em auto; padding: 20px 50px;
|
||||
background-color: #fff; border-radius: 1em;
|
||||
}
|
||||
label { display: inline-block; min-width: 7em; }
|
||||
input { border: 1px solid #ccc; padding: 0.4em; margin: 0 0 10px 0; }
|
||||
a:link, a:visited { color: #69c; text-decoration: none; }
|
||||
@media (max-width: 700px) {
|
||||
body { background-color: #fff; }
|
||||
div.content {
|
||||
width: auto; margin: 0 auto; border-radius: 0; padding: 1em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<body>
|
||||
<div class="content">
|
||||
<h1>Mongoose Cookie Based Authentication</h1>
|
||||
<p>Use name "Joe", password "Doe" to login.</p>
|
||||
<form method="POST">
|
||||
<div>
|
||||
<label>Name:</label>
|
||||
<input type="text" name="name"/>
|
||||
</div><div>
|
||||
<label>Password:</label>
|
||||
<input type="password" name="password"/>
|
||||
</div><div>
|
||||
<input type="submit" value="Login"/>
|
||||
</div>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
@ -1,43 +0,0 @@
|
||||
// This file is part of mongoose web server project,
|
||||
// https://github.com/cesanta/mongoose
|
||||
|
||||
using System;
|
||||
|
||||
public class Program {
|
||||
static private int EventHandler(IntPtr conn_ptr, int ev) {
|
||||
MongooseConnection conn = (MongooseConnection)
|
||||
System.Runtime.InteropServices.Marshal.PtrToStructure(
|
||||
conn_ptr , typeof(MongooseConnection));
|
||||
|
||||
if (ev == 102) {
|
||||
// MG_AUTH
|
||||
return 1;
|
||||
} else if (ev == 103) {
|
||||
// MG_REQUEST
|
||||
Mongoose.send_data(conn_ptr, "Hello from C#!\n");
|
||||
Mongoose.send_data(conn_ptr, "URI: " + conn.uri + "\n");
|
||||
Mongoose.send_data(conn_ptr, "HTTP Headers:\n");
|
||||
|
||||
for (int i = 0; i < conn.num_headers; i++) {
|
||||
IntPtr name = conn.http_headers[i].name;
|
||||
IntPtr val = conn.http_headers[i].value;
|
||||
System.Runtime.InteropServices.Marshal.PtrToStringAnsi(name);
|
||||
Mongoose.send_data(conn_ptr, " " +
|
||||
System.Runtime.InteropServices.Marshal.PtrToStringAnsi(name) + ": " +
|
||||
System.Runtime.InteropServices.Marshal.PtrToStringAnsi(val) + "\n");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void Main() {
|
||||
Mongoose web_server = new Mongoose(".", "9001",
|
||||
new MongooseEventHandler(EventHandler));
|
||||
|
||||
Console.WriteLine("Mongoose started, press Ctrl-C to exit.");
|
||||
for (;;) {
|
||||
web_server.poll(1000);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
// This file is part of mongoose web server project,
|
||||
// https://github.com/cesanta/mongoose
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)] public struct MongooseHeader {
|
||||
[MarshalAs(UnmanagedType.LPTStr)] public IntPtr name;
|
||||
[MarshalAs(UnmanagedType.LPTStr)] public IntPtr value;
|
||||
};
|
||||
|
||||
// mongoose.h :: struct mg_connection
|
||||
[StructLayout(LayoutKind.Sequential)] public struct MongooseConnection {
|
||||
[MarshalAs(UnmanagedType.LPTStr)] public string request_method;
|
||||
[MarshalAs(UnmanagedType.LPTStr)] public string uri;
|
||||
[MarshalAs(UnmanagedType.LPTStr)] public string http_version;
|
||||
[MarshalAs(UnmanagedType.LPTStr)] public string query_string;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray,SizeConst=48)] public char[] remote_ip;
|
||||
[MarshalAs(UnmanagedType.LPTStr)] public string local_ip;
|
||||
[MarshalAs(UnmanagedType.U2)] public short remote_port;
|
||||
[MarshalAs(UnmanagedType.U2)] public short local_port;
|
||||
|
||||
[MarshalAs(UnmanagedType.SysInt)] public int num_headers;
|
||||
[MarshalAs(UnmanagedType.ByValArray,SizeConst=30)]
|
||||
public MongooseHeader[] http_headers;
|
||||
|
||||
[MarshalAs(UnmanagedType.LPTStr)] public IntPtr content;
|
||||
[MarshalAs(UnmanagedType.SysInt)] public int content_len;
|
||||
|
||||
[MarshalAs(UnmanagedType.SysInt)] public int is_websocket;
|
||||
[MarshalAs(UnmanagedType.SysInt)] public int status_code;
|
||||
[MarshalAs(UnmanagedType.SysInt)] public int wsbits;
|
||||
};
|
||||
|
||||
public delegate int MongooseEventHandler(IntPtr c, int ev);
|
||||
|
||||
public class Mongoose {
|
||||
public const string dll_ = "mongoose";
|
||||
private IntPtr server_;
|
||||
|
||||
[DllImport(dll_)] private static extern IntPtr
|
||||
mg_create_server(IntPtr user_data, MongooseEventHandler eh);
|
||||
[DllImport(dll_)] private static extern int
|
||||
mg_poll_server(IntPtr server, int milli);
|
||||
[DllImport(dll_)] private static extern IntPtr
|
||||
mg_set_option(IntPtr server, string name, string value);
|
||||
[DllImport(dll_)] public static extern int
|
||||
mg_send_data(IntPtr conn, string data, int length);
|
||||
|
||||
public Mongoose(string document_root,
|
||||
string listening_port,
|
||||
MongooseEventHandler event_handler) {
|
||||
server_ = mg_create_server(IntPtr.Zero, event_handler);
|
||||
mg_set_option(server_, "document_root", document_root);
|
||||
mg_set_option(server_, "listening_port", listening_port);
|
||||
}
|
||||
|
||||
public static int send_data(IntPtr conn, string data) {
|
||||
return mg_send_data(conn, data, data.Length);
|
||||
}
|
||||
|
||||
public void poll(int milli) {
|
||||
mg_poll_server(server_, milli);
|
||||
}
|
||||
|
||||
// TODO: add destructor and call mg_destroy_server()
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
# Copyright (c) 2014 Cesanta Software
|
||||
# All rights reserved
|
||||
|
||||
PROG = digest_auth
|
||||
CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
|
||||
SOURCES = $(PROG).c ../../mongoose.c
|
||||
|
||||
$(PROG): $(SOURCES)
|
||||
$(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
|
||||
|
||||
clean:
|
||||
rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
|
@ -1,36 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "mongoose.h"
|
||||
|
||||
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||
|
||||
if (ev == MG_AUTH) {
|
||||
int result = MG_FALSE; // Not authorized
|
||||
FILE *fp;
|
||||
|
||||
// To populate passwords file, do
|
||||
// mongoose -A my_passwords.txt mydomain.com admin admin
|
||||
if ((fp = fopen("my_passwords.txt", "r")) != NULL) {
|
||||
result = mg_authorize_digest(conn, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return MG_FALSE;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_server *server = mg_create_server(NULL, ev_handler);
|
||||
mg_set_option(server, "listening_port", "8080");
|
||||
mg_set_option(server, "document_root", ".");
|
||||
|
||||
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
|
||||
for (;;) {
|
||||
mg_poll_server(server, 1000);
|
||||
}
|
||||
mg_destroy_server(&server);
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
# Copyright (c) 2014 Cesanta Software
|
||||
# All rights reserved
|
||||
|
||||
PROG = file_upload
|
||||
CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
|
||||
SOURCES = $(PROG).c ../../mongoose.c
|
||||
|
||||
all: $(PROG)
|
||||
|
||||
run: $(PROG)
|
||||
./$(PROG)
|
||||
|
||||
$(PROG): $(SOURCES) Makefile
|
||||
$(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
|
||||
|
||||
win:
|
||||
wine cl $(SOURCES) /MD /nologo /DNDEBUG /O1 /I../.. /Fe$(PROG).exe
|
||||
|
||||
clean:
|
||||
rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib *.gc*
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user