mv dirname

This commit is contained in:
JamesonHuang 2015-07-04 23:06:13 +08:00
parent ceea7d2352
commit d3ffd46065
1483 changed files with 444747 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 446 KiB

View File

@ -0,0 +1,249 @@
/************************************************
*
* 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_

View File

@ -0,0 +1,249 @@
/************************************************
*
* 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_

View File

@ -0,0 +1,322 @@
/************************************************
*
* 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_

View File

@ -0,0 +1,11 @@
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

View File

@ -0,0 +1,246 @@
/************************************************
*
* 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_

View File

@ -0,0 +1,66 @@
/************************************************
*
* 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;
}

View File

@ -0,0 +1 @@
gcc -o main main.c

View File

@ -0,0 +1,61 @@
!_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

View File

@ -0,0 +1 @@
[Oct-27-2012] fix a nonexitent address cause segmentation fault (core dumped)

View File

@ -0,0 +1,28 @@
#
# "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

View File

@ -0,0 +1,11 @@
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>

View File

@ -0,0 +1,7 @@
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.

View File

@ -0,0 +1,32 @@
#
# "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

View File

@ -0,0 +1,27 @@
/*
"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 */

View File

@ -0,0 +1,104 @@
/*
"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;
}

View File

@ -0,0 +1,30 @@
/*
"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 */

View File

@ -0,0 +1,149 @@
/*
"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;
}

View File

@ -0,0 +1,29 @@
/*
"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 */

View File

@ -0,0 +1,112 @@
/*
"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;
}

View File

@ -0,0 +1,31 @@
/*
"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 */

View File

@ -0,0 +1,53 @@
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

View File

@ -0,0 +1,69 @@
*****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.

View File

@ -0,0 +1,6 @@
LISTEN 8080
#comment
#DocumentRoot /home/gaurav/Dropbox/Code
CACHESIZE 20000000
CACHETIME 60
IPADDR 10.2.69.8

View File

@ -0,0 +1,5 @@
#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

View File

@ -0,0 +1,42 @@
#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

View File

@ -0,0 +1,12 @@
#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

View File

@ -0,0 +1,7 @@
#ifndef __TOKENIZE_HDR__
#define __TOKENIZE_HDR__
#define MAX_TOKEN_SIZE 2048
int tokenize(char **str, char *dest);
#endif

View File

@ -0,0 +1,82 @@
#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

View File

@ -0,0 +1,14 @@
<?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>

View File

@ -0,0 +1,13 @@
<!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 :. &copy; Graformix</title>
</head>
<body>
JUST A TEST FILE!
</body>
</html>

View File

@ -0,0 +1,50 @@
/*
* 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;
}

View File

@ -0,0 +1,561 @@
/*
* 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;
}

View File

@ -0,0 +1,147 @@
/*
* 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

View File

@ -0,0 +1,111 @@
/*
* 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;
}

View File

@ -0,0 +1,55 @@
/*
* 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;
}

View File

@ -0,0 +1,332 @@
/*
* 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

View File

@ -0,0 +1,16 @@
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

View File

@ -0,0 +1,6 @@
HTTPProxy
Description:
HTTPProxy is a basic proxy server for http.

View File

@ -0,0 +1,33 @@
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.

View File

@ -0,0 +1,227 @@
#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;
}

View File

@ -0,0 +1,15 @@
#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

View File

@ -0,0 +1,28 @@
#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);
}

View File

@ -0,0 +1,8 @@
#ifndef LIST_HH
#define LIST_HH
#include "proxy.h"
const char *list_get_key(struct METADATA_HEAD *list, const char *key);
#endif

View File

@ -0,0 +1,313 @@
#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&ouml;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;
}

View File

@ -0,0 +1,194 @@
#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;
}

View File

@ -0,0 +1,8 @@
#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

View File

@ -0,0 +1,59 @@
#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

View File

@ -0,0 +1,50 @@
#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();
}

View File

@ -0,0 +1,249 @@
/************************************************
*
* 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_

View File

@ -0,0 +1,249 @@
/************************************************
*
* 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_

View File

@ -0,0 +1,16 @@
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/>.

View File

@ -0,0 +1,322 @@
/************************************************
*
* 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_

View File

@ -0,0 +1,78 @@
# <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

View File

@ -0,0 +1,246 @@
/************************************************
*
* 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_

View File

@ -0,0 +1,249 @@
# 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.

View File

@ -0,0 +1,27 @@
# 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.

View File

@ -0,0 +1,34 @@
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)

View File

@ -0,0 +1,173 @@
# 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

View File

@ -0,0 +1,45 @@
# 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 cant 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.

View File

@ -0,0 +1,18 @@
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)

View File

@ -0,0 +1,44 @@
# 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.

View File

@ -0,0 +1,154 @@
# 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.

View File

@ -0,0 +1,49 @@
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)

View File

@ -0,0 +1,218 @@
# 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)

View File

@ -0,0 +1,70 @@
# 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.

View File

@ -0,0 +1,86 @@
# 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

View File

@ -0,0 +1 @@
*.exe

View File

@ -0,0 +1,20 @@
# 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

View File

@ -0,0 +1,21 @@
# 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*

View File

@ -0,0 +1,45 @@
// 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;
}

View File

@ -0,0 +1,12 @@
# 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

View File

@ -0,0 +1,84 @@
#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;
}

View File

@ -0,0 +1,12 @@
# 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

View File

@ -0,0 +1,97 @@
// 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;
}

View File

@ -0,0 +1,33 @@
<!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>

View File

@ -0,0 +1,44 @@
<!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>

View File

@ -0,0 +1,43 @@
// 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);
}
}
}

View File

@ -0,0 +1,68 @@
// 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()
}

View File

@ -0,0 +1,12 @@
# 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

View File

@ -0,0 +1,36 @@
#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;
}

View File

@ -0,0 +1,20 @@
# 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