1175 lines
31 KiB
C++
Executable File
1175 lines
31 KiB
C++
Executable File
/* J. David's webserver */
|
||
/* This is a simple webserver.
|
||
* Created November 1999 by J. David Blackstone.
|
||
* CSE 4344 (Network concepts), Prof. Zeigler
|
||
* University of Texas at Arlington
|
||
*/
|
||
/* This program compiles for Sparc Solaris 2.6.
|
||
* To compile for Linux:
|
||
* 1) Comment out the #include <pthread.h> line.
|
||
* 2) Comment out the line that defines the variable newthread.
|
||
* 3) Comment out the two lines that run pthread_create().
|
||
* 4) Uncomment the line that runs accept_request().
|
||
* 5) Remove -lsocket from the Makefile.
|
||
*/
|
||
#include <stdio.h>
|
||
#include <sys/socket.h>
|
||
#include <sys/types.h>
|
||
#include <netinet/in.h>
|
||
#include <arpa/inet.h>
|
||
#include <unistd.h>
|
||
#include <ctype.h>
|
||
#include <strings.h>
|
||
#include <string.h>
|
||
#include <sys/stat.h>
|
||
#include <pthread.h>
|
||
#include <sys/wait.h>
|
||
#include <stdlib.h>
|
||
#include <errno.h>
|
||
|
||
|
||
#include "httpd.h"
|
||
#define LOG_TAG "httpserver"
|
||
#ifdef SDV
|
||
#include <cutils/log.h>
|
||
#warning "BUILD SDV HTTPD"
|
||
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG , LOG_TAG, __VA_ARGS__)
|
||
#else
|
||
#define LOGD(...) printf(__VA_ARGS__)
|
||
#endif
|
||
typedef struct mime_typ {
|
||
char ext[10];
|
||
int ext_len;
|
||
char value[50];
|
||
} mime_typ_t;
|
||
static mime_typ_t mime_tab[] = {
|
||
{"txt", 3, "txt/plain"},
|
||
{"ms", 2, "application/x-troff-ms"},
|
||
{"ms", 2, "application/x-troff-ms"},
|
||
{"mv", 2, "video/x-sgi-movie"},
|
||
{"qt", 2, "video/quicktime"},
|
||
{"ts", 2, "video/mp2t"},
|
||
{"3gp", 3, "video/3gp"},
|
||
{"asf", 3, "video/x-ms-asf"},
|
||
{"avi", 3, "video/x-msvideo"},
|
||
{"flv", 3, "video/x-flv"},
|
||
{"vob", 3, "video/mpeg"},
|
||
{"mkv", 3, "video/x-matroska"},
|
||
{"mov", 3, "video/quicktime"},
|
||
{"mp2", 3, "audio/mpeg"},
|
||
{"mp3", 3, "audio/mpeg"},
|
||
{"mp4", 3, "video/mp4"},
|
||
{"mpe", 3, "video/mpeg"},
|
||
{"3gp", 3, "video/mpeg"},
|
||
{"msh", 3, "model/mesh"},
|
||
{"mpg", 3, "video/mpeg"},
|
||
{"dat", 3, "video/mpeg"},
|
||
{"mpeg", 4, "video/mpeg"},
|
||
{"mpga", 4, "audio/mpeg"},
|
||
{"m2ts", 4, "video/mp2t"},
|
||
{"movie", 5, "video/x-sgi-movie"},
|
||
{"png", 3, "image/png"},
|
||
{"bmp", 3, "image/bmp"},
|
||
{"gif", 3, "image/gif"},
|
||
{"jpe", 3, "image/jpeg"},
|
||
{"jpg", 3, "image/jpeg"},
|
||
{"jpeg", 4, "image/jpeg"},
|
||
{"jfif", 4, "image/jpeg"},
|
||
{"ra", 2, "audio/x-realaudio"},
|
||
{"rmvb", 4, "appllication/vnd.rn-realmedia"},
|
||
{"rm", 2, "appllication/vnd.rn-realmedia"},
|
||
{"mp2", 3, "audio/mpeg"},
|
||
{"mp3", 3, "audio/mpeg"},
|
||
{"ram", 3, "audio/x-pn-realaudio"},
|
||
{"wav", 3, "audio/x-wav"},
|
||
{"wax", 3, "audio/x-ms-wax"},
|
||
{"wma", 3, "audio/x-ms-wma"},
|
||
{"wmv", 3, "video/x-ms-wmv"},
|
||
{"mpga", 4, "audio/mpeg"},
|
||
{"", 0, ""},
|
||
};
|
||
static int startFlag = 0;
|
||
static pthread_t acceptThreadId;
|
||
static httpdCallback gHttpdCallback = {NULL, NULL, NULL};
|
||
|
||
#define ISspace(x) isspace((int)(x))
|
||
|
||
#define SERVER_STRING "Server: jdbhttpd/0.1.0\r\n"
|
||
|
||
void *accept_request(void* client);
|
||
void bad_request(int);
|
||
void cat(int, FILE *);
|
||
void cannot_execute(int);
|
||
void error_die(const char *);
|
||
void execute_cgi(int, const char *, const char *, const char *);
|
||
int get_line(int, char *, int);
|
||
void headers(int, const char *);
|
||
void not_found(int);
|
||
void serve_file(int, const char *);
|
||
int startup(u_short *);
|
||
void unimplemented(int);
|
||
void requested_range_not_satisfiable(int sockfd);
|
||
void serve_whole_file(int sockfd, const char *filename, FILE *resource);
|
||
void serve_partial_file(int sockfd, const char *filename, FILE *resource, long long startPos, long long endPos, long long fileSize);
|
||
void partial_headers(int sockfd, const char *filename, long long startPos, long long endPos);
|
||
void partial_cat(int sockfd, FILE *resource, long long startPos, long long endPos, long long fileSize);
|
||
ssize_t recv_wrap(int __fd, void *__buf, size_t __n, int __flags);
|
||
ssize_t send_wrap(int __fd, __const void *__buf, size_t __n, int __flags);
|
||
|
||
|
||
ssize_t recv_wrap(int __fd, void *__buf, size_t __n, int __flags)
|
||
{
|
||
int ret = 0;
|
||
|
||
fd_set rfd;
|
||
FD_ZERO(&rfd);
|
||
FD_SET(__fd, &rfd);
|
||
struct timeval to = {1, 0};
|
||
int result = select(__fd+1, &rfd, NULL, NULL, &to);
|
||
if (result > 0)
|
||
{
|
||
ret = recv(__fd, __buf, __n, __flags);
|
||
}
|
||
else
|
||
{
|
||
LOGD("error!, recv time out:1 sec pass\n");
|
||
ret = -1;
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
ssize_t send_wrap(int __fd, __const void *__buf, size_t __n, int __flags)
|
||
{
|
||
int ret = 0;
|
||
|
||
fd_set wfd;
|
||
FD_ZERO(&wfd);
|
||
FD_SET(__fd, &wfd);
|
||
struct timeval to = {1, 0};
|
||
int result = 1;
|
||
result = select(__fd+1, NULL, &wfd, NULL, &to);
|
||
if(result > 0)
|
||
{
|
||
ret = send(__fd, __buf, __n, __flags);
|
||
}
|
||
else
|
||
{
|
||
LOGD("error!, send time out:1 sec pass\n");
|
||
ret = -1;
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
/**********************************************************************/
|
||
/* A request has caused a call to accept() on the server port to
|
||
* return. Process the request appropriately.
|
||
* Parameters: the socket connected to the client */
|
||
/**********************************************************************/
|
||
void *accept_request(void* client)
|
||
{
|
||
int client_sock = *(int*)client;
|
||
delete (int*)client;
|
||
char buf[1024];
|
||
int numchars;
|
||
char method[255];
|
||
char url[255];
|
||
char path[512];
|
||
size_t i, j;
|
||
struct stat st;
|
||
int cgi = 0; /* becomes true if server decides this is a CGI
|
||
* program */
|
||
char *query_string = NULL;
|
||
|
||
int snd_size;
|
||
socklen_t optlen;
|
||
int ret;
|
||
|
||
optlen = sizeof(snd_size);
|
||
ret = getsockopt(client_sock, SOL_SOCKET, SO_SNDBUF, (char*)&snd_size, &optlen);
|
||
if(ret < 0)
|
||
{
|
||
LOGD("getsockopt failed\n");
|
||
}
|
||
else
|
||
{
|
||
LOGD("\ndefault snd buf:%d %d\n", snd_size, optlen);
|
||
|
||
// snd_size = 64*1024;
|
||
snd_size /= 2;
|
||
ret = setsockopt(client_sock, SOL_SOCKET, SO_SNDBUF, (const char*)&snd_size, optlen);
|
||
|
||
if(ret < 0)
|
||
{
|
||
LOGD("setsockopt failed\n");
|
||
}
|
||
else
|
||
{
|
||
ret = getsockopt(client_sock, SOL_SOCKET, SO_SNDBUF, (char*)&snd_size, &optlen);
|
||
if(ret < 0)
|
||
{
|
||
LOGD("getsockopt failed tt\n");
|
||
}
|
||
else
|
||
{
|
||
LOGD("set snd buf:%d %d\n", snd_size, optlen);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
numchars = get_line(client_sock, buf, sizeof(buf));
|
||
i = 0; j = 0;
|
||
|
||
LOGD("accept_request, get_line:%s", buf);
|
||
while (!ISspace(buf[j]) && (i < sizeof(method) - 1))
|
||
{
|
||
method[i] = buf[j];
|
||
i++; j++;
|
||
}
|
||
method[i] = '\0';
|
||
LOGD("accept_request, method:%s", method);
|
||
if (strcasecmp(method, "GET") && strcasecmp(method, "POST"))
|
||
{
|
||
LOGD("unimplemented");
|
||
unimplemented(client_sock);
|
||
return NULL;
|
||
}
|
||
|
||
if (strcasecmp(method, "POST") == 0)
|
||
cgi = 1;
|
||
|
||
i = 0;
|
||
while (ISspace(buf[j]) && (j < sizeof(buf)))
|
||
j++;
|
||
while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf)))
|
||
{
|
||
url[i] = buf[j];
|
||
i++; j++;
|
||
}
|
||
url[i] = '\0';
|
||
|
||
if (strcasecmp(method, "GET") == 0)
|
||
{
|
||
query_string = url;
|
||
while ((*query_string != '?') && (*query_string != '\0'))
|
||
query_string++;
|
||
if (*query_string == '?')
|
||
{
|
||
cgi = 1;
|
||
*query_string = '\0';
|
||
query_string++;
|
||
}
|
||
}
|
||
|
||
sprintf(path, "%s", url);
|
||
if (path[strlen(path) - 1] == '/')
|
||
strcat(path, "index.html");
|
||
if (stat(path, &st) == -1 && strcmp(method, "POST") != 0) {
|
||
while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */
|
||
numchars = get_line(client_sock, buf, sizeof(buf));
|
||
not_found(client_sock);
|
||
}
|
||
else
|
||
{
|
||
if (!cgi) {
|
||
LOGD("exe serve_file, path:%s", path);
|
||
serve_file(client_sock, path);
|
||
}
|
||
else {
|
||
LOGD("exe execute_cgi, path:%s, method:%s, query_string:%s", path, method, query_string);
|
||
execute_cgi(client_sock, path, method, query_string);
|
||
}
|
||
}
|
||
|
||
close(client_sock);
|
||
return NULL;
|
||
}
|
||
|
||
/**********************************************************************/
|
||
/* Inform the client that a request it has made has a problem.
|
||
* Parameters: client socket */
|
||
/**********************************************************************/
|
||
void bad_request(int client)
|
||
{
|
||
char buf[1024];
|
||
|
||
sprintf(buf, "HTTP/1.0 400 BAD REQUEST\r\n");
|
||
send_wrap(client, buf, sizeof(buf), 0);
|
||
sprintf(buf, "Content-type: text/html\r\n");
|
||
send_wrap(client, buf, sizeof(buf), 0);
|
||
sprintf(buf, "\r\n");
|
||
send_wrap(client, buf, sizeof(buf), 0);
|
||
sprintf(buf, "<P>Your browser sent a bad request, ");
|
||
send_wrap(client, buf, sizeof(buf), 0);
|
||
sprintf(buf, "such as a POST without a Content-Length.\r\n");
|
||
send_wrap(client, buf, sizeof(buf), 0);
|
||
}
|
||
|
||
/**********************************************************************/
|
||
/* Put the entire contents of a file out on a socket. This function
|
||
* is named after the UNIX "cat" command, because it might have been
|
||
* easier just to do something like pipe, fork, and exec("cat").
|
||
* Parameters: the client socket descriptor
|
||
* FILE pointer for the file to cat */
|
||
/**********************************************************************/
|
||
void cat(int client, FILE *resource)
|
||
{
|
||
#if 1
|
||
char buf[1024];
|
||
int readlen;
|
||
int sendret;
|
||
|
||
while((readlen = fread(buf, sizeof(char), 1024, resource)))
|
||
{
|
||
//printf("i = %d, readlen = %d\n", ++i, readlen);
|
||
sendret = send_wrap(client, buf, readlen, 0);
|
||
if(sendret < 0)
|
||
{
|
||
LOGD("[cat] send error! [%s]", strerror(errno));
|
||
return;
|
||
}
|
||
}
|
||
/* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݻ<EFBFBD><DDBB>ߴ<EFBFBD><DFB4><EFBFBD> */
|
||
if(!feof(resource))
|
||
{
|
||
LOGD("[cat] end before eof");
|
||
return;
|
||
}
|
||
#else
|
||
char buf[1024];
|
||
time_t startTime, curTime;
|
||
time(&startTime);
|
||
while (!feof(resource))
|
||
{
|
||
int sendlen = fread(buf, 1, sizeof(buf), resource);
|
||
if (sendlen < 0) {
|
||
LOGD("read error");
|
||
break;
|
||
}
|
||
|
||
int ret = 0;
|
||
while (ret < sendlen) {
|
||
time(&curTime);
|
||
if (curTime - startTime > 3) {
|
||
LOGD("send nothing in 3 seconds, exit thread");
|
||
return;
|
||
}
|
||
|
||
int sndret = send(client, buf+ret, sendlen-ret, 0);
|
||
if (sndret < 0) {
|
||
LOGD("send error:[%s]", strerror(errno));
|
||
signal(SIGPIPE, SIG_IGN);
|
||
usleep(100*1000);
|
||
continue;
|
||
}
|
||
time(&startTime);
|
||
ret += sndret;
|
||
}
|
||
}
|
||
#endif
|
||
LOGD("send file finish");
|
||
}
|
||
|
||
/**********************************************************************/
|
||
/* Inform the client that a CGI script could not be executed.
|
||
* Parameter: the client socket descriptor. */
|
||
/**********************************************************************/
|
||
void cannot_execute(int client)
|
||
{
|
||
char buf[1024];
|
||
|
||
sprintf(buf, "HTTP/1.0 500 Internal Server Error\r\n");
|
||
send_wrap(client, buf, strlen(buf), 0);
|
||
sprintf(buf, "Content-type: text/html\r\n");
|
||
send_wrap(client, buf, strlen(buf), 0);
|
||
sprintf(buf, "\r\n");
|
||
send_wrap(client, buf, strlen(buf), 0);
|
||
sprintf(buf, "<P>Error prohibited CGI execution.\r\n");
|
||
send_wrap(client, buf, strlen(buf), 0);
|
||
}
|
||
|
||
/**********************************************************************/
|
||
/* Print out an error message with perror() (for system errors; based
|
||
* on value of errno, which indicates system call errors) and exit the
|
||
* program indicating an error. */
|
||
/**********************************************************************/
|
||
void error_die(const char *sc)
|
||
{
|
||
perror(sc);
|
||
exit(1);
|
||
}
|
||
|
||
/**********************************************************************/
|
||
/* Execute a CGI script. Will need to set environment variables as
|
||
* appropriate.
|
||
* Parameters: client socket descriptor
|
||
* path to the CGI script */
|
||
/**********************************************************************/
|
||
void execute_cgi(int client, const char *path,
|
||
const char *method, const char *query_string)
|
||
{
|
||
char buf[1024];
|
||
int cgi_output[2];
|
||
int cgi_input[2];
|
||
pid_t pid;
|
||
int status;
|
||
int i;
|
||
char c;
|
||
int numchars = 1;
|
||
int content_length = -1;
|
||
|
||
buf[0] = 'A'; buf[1] = '\0';
|
||
if (strcasecmp(method, "GET") == 0)
|
||
while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */
|
||
numchars = get_line(client, buf, sizeof(buf));
|
||
else /* POST */
|
||
{
|
||
numchars = get_line(client, buf, sizeof(buf));
|
||
LOGD("buf:%s", buf);
|
||
while ((numchars > 0) && strcmp("\n", buf))
|
||
{
|
||
LOGD("buf:%s", buf);
|
||
buf[15] = '\0';
|
||
if (strcasecmp(buf, "Content-Length:") == 0)
|
||
content_length = atoi(&(buf[16]));
|
||
numchars = get_line(client, buf, sizeof(buf));
|
||
}
|
||
if (content_length == -1) {
|
||
bad_request(client);
|
||
return;
|
||
}
|
||
}
|
||
|
||
sprintf(buf, "HTTP/1.0 200 OK\r\n");
|
||
send_wrap(client, buf, strlen(buf), 0);
|
||
const char *filename = strrchr(path, '/');
|
||
if (filename != NULL) {
|
||
filename++;
|
||
} else {
|
||
filename = (char*)path;
|
||
}
|
||
if (gHttpdCallback.httpdStatCallback != NULL) {
|
||
gHttpdCallback.httpdStatCallback(POST_FILE_START, (void*)filename, strlen(filename)+1, gHttpdCallback.handler);
|
||
}
|
||
int recvLen = 0;
|
||
time_t recvTime;
|
||
time(&recvTime);
|
||
while (recvLen < content_length) {
|
||
time_t recvTimeout;
|
||
time(&recvTimeout);
|
||
if (recvTimeout - recvTime > 1) {
|
||
if (gHttpdCallback.httpdStatCallback != NULL) {
|
||
gHttpdCallback.httpdStatCallback(POST_FILE_ERROR, (void*)filename, strlen(filename)+1, gHttpdCallback.handler);
|
||
}
|
||
return ;
|
||
}
|
||
fd_set rfd;
|
||
FD_ZERO(&rfd);
|
||
FD_SET(client, &rfd);
|
||
struct timeval tv = {0, 100000};
|
||
int sResult = select(client + 1, &rfd, NULL, NULL, &tv);
|
||
if (sResult > 0) {
|
||
if (FD_ISSET(client, &rfd) != 0) {
|
||
int recvRet = recv_wrap(client, buf, 1024, 0);
|
||
if (recvRet < 0) {
|
||
LOGD("recv error, errno [%d]", errno);
|
||
if (gHttpdCallback.httpdStatCallback != NULL) {
|
||
gHttpdCallback.httpdStatCallback(POST_FILE_ERROR, (void*)filename,
|
||
strlen(filename)+1, gHttpdCallback.handler);
|
||
}
|
||
return ;
|
||
}
|
||
// LOGD("recv success, recvRet [%d], buf[%s]", recvRet, buf);
|
||
if (gHttpdCallback.httpdDataCallback != NULL) {
|
||
gHttpdCallback.httpdDataCallback((void*)buf, recvRet, gHttpdCallback.handler);
|
||
}
|
||
recvLen += recvRet;
|
||
time(&recvTime);
|
||
}
|
||
}
|
||
}
|
||
if (gHttpdCallback.httpdStatCallback != NULL) {
|
||
gHttpdCallback.httpdStatCallback(POST_FILE_END, (void*)filename, strlen(filename)+1, gHttpdCallback.handler);
|
||
}
|
||
#if 0
|
||
if (pipe(cgi_output) < 0) {
|
||
cannot_execute(client);
|
||
return;
|
||
}
|
||
if (pipe(cgi_input) < 0) {
|
||
cannot_execute(client);
|
||
return;
|
||
}
|
||
|
||
if ( (pid = fork()) < 0 ) {
|
||
cannot_execute(client);
|
||
return;
|
||
}
|
||
if (pid == 0) /* child: CGI script */
|
||
{
|
||
char meth_env[255];
|
||
char query_env[255];
|
||
char length_env[255];
|
||
|
||
dup2(cgi_output[1], 1);
|
||
dup2(cgi_input[0], 0);
|
||
close(cgi_output[0]);
|
||
close(cgi_input[1]);
|
||
sprintf(meth_env, "REQUEST_METHOD=%s", method);
|
||
putenv(meth_env);
|
||
if (strcasecmp(method, "GET") == 0) {
|
||
sprintf(query_env, "QUERY_STRING=%s", query_string);
|
||
putenv(query_env);
|
||
}
|
||
else { /* POST */
|
||
sprintf(length_env, "CONTENT_LENGTH=%d", content_length);
|
||
putenv(length_env);
|
||
}
|
||
execl(path, path, NULL);
|
||
exit(0);
|
||
} else { /* parent */
|
||
close(cgi_output[1]);
|
||
close(cgi_input[0]);
|
||
if (strcasecmp(method, "POST") == 0)
|
||
for (i = 0; i < content_length; i++) {
|
||
recv_wrap(client, &c, 1, 0);
|
||
write(cgi_input[1], &c, 1);
|
||
}
|
||
while (read(cgi_output[0], &c, 1) > 0)
|
||
send(client, &c, 1, 0);
|
||
|
||
close(cgi_output[0]);
|
||
close(cgi_input[1]);
|
||
waitpid(pid, &status, 0);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
/**********************************************************************/
|
||
/* Get a line from a socket, whether the line ends in a newline,
|
||
* carriage return, or a CRLF combination. Terminates the string read
|
||
* with a null character. If no newline indicator is found before the
|
||
* end of the buffer, the string is terminated with a null. If any of
|
||
* the above three line terminators is read, the last character of the
|
||
* string will be a linefeed and the string will be terminated with a
|
||
* null character.
|
||
* Parameters: the socket descriptor
|
||
* the buffer to save the data in
|
||
* the size of the buffer
|
||
* Returns: the number of bytes stored (excluding null) */
|
||
/**********************************************************************/
|
||
int get_line(int sock, char *buf, int size)
|
||
{
|
||
int i = 0;
|
||
char c = '\0';
|
||
int n;
|
||
|
||
while ((i < size - 1) && (c != '\n'))
|
||
{
|
||
n = recv_wrap(sock, &c, 1, 0);
|
||
/* DEBUG printf("%02X\n", c); */
|
||
if (n > 0)
|
||
{
|
||
if (c == '\r')
|
||
{
|
||
n = recv_wrap(sock, &c, 1, MSG_PEEK);
|
||
/* DEBUG printf("%02X\n", c); */
|
||
if ((n > 0) && (c == '\n'))
|
||
recv_wrap(sock, &c, 1, 0);
|
||
else
|
||
c = '\n';
|
||
}
|
||
buf[i] = c;
|
||
i++;
|
||
}
|
||
else
|
||
c = '\n';
|
||
}
|
||
buf[i] = '\0';
|
||
|
||
return(i);
|
||
}
|
||
|
||
/**********************************************************************/
|
||
/* Return the informational HTTP headers about a file. */
|
||
/* Parameters: the socket to print the headers on
|
||
* the name of the file */
|
||
/**********************************************************************/
|
||
void headers(int client, const char *filename)
|
||
{
|
||
#if 1
|
||
char buf[1024];
|
||
char contentType[128];
|
||
char suffix[16];
|
||
char *pos = NULL;
|
||
int i;
|
||
struct stat st;
|
||
(void)filename; /* could use filename to determine file type */
|
||
|
||
|
||
/* <20><><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD><EFBFBD> */
|
||
pos = (char*)strrchr(filename, '.');
|
||
if(pos != NULL)
|
||
{
|
||
pos++;
|
||
for(i = 0; mime_tab[i].ext_len != 0; i++)
|
||
{
|
||
if(strcmp(pos, mime_tab[i].ext) == 0)
|
||
{
|
||
strcpy(contentType, mime_tab[i].value);
|
||
break;
|
||
}
|
||
}
|
||
if(mime_tab[i].ext_len == 0)
|
||
{
|
||
strcpy(contentType, "application/octet-stream");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
strcpy(contentType, "application/octet-stream");
|
||
}
|
||
/* <20><>ȡ<EFBFBD>ļ<EFBFBD><C4BC><EFBFBD>Ϣ */
|
||
stat(filename, &st);
|
||
|
||
LOGD("response[");
|
||
strcpy(buf, "HTTP/1.1 200 OK\r\n");
|
||
LOGD("%s", buf);
|
||
send_wrap(client, buf, strlen(buf), 0);
|
||
strcpy(buf, SERVER_STRING);
|
||
LOGD("%s", buf);
|
||
send_wrap(client, buf, strlen(buf), 0);
|
||
//sprintf(buf, "Content-Type: text/html\r\n");
|
||
sprintf(buf, "Content-Type: %s\r\n", contentType);
|
||
LOGD("%s", buf);
|
||
send_wrap(client, buf, strlen(buf), 0);
|
||
sprintf(buf, "Content-Length: %lld\r\n", st.st_size);
|
||
LOGD("%s", buf);
|
||
send_wrap(client, buf, strlen(buf), 0);
|
||
sprintf(buf, "Connection: Keep-alive\r\n");
|
||
LOGD("%s", buf);
|
||
send_wrap(client, buf, strlen(buf), 0);
|
||
strcpy(buf, "\r\n");
|
||
LOGD("%s", buf);
|
||
send_wrap(client, buf, strlen(buf), 0);
|
||
LOGD("]");
|
||
|
||
#else
|
||
char buf[1024] = "";
|
||
int i = 0;
|
||
struct stat st;
|
||
char *ext = strrchr(filename, '.')+sizeof('.');
|
||
(void)filename; /* could use filename to determine file type */
|
||
stat(filename, &st);
|
||
for (i = 0; mime_tab[i].ext != NULL; i++)
|
||
{
|
||
if (strcmp(mime_tab[i].ext, ext) == 0)
|
||
{
|
||
snprintf(buf, 1024, "HTTP/1.1 206 Partial Content\r\nContent-Type: %s\r\nContent-Length: %lld\r\nContent-Ranges: bytes 0-%lld/%lld\r\nConnection: Keep-alive\r\n\r\n",
|
||
mime_tab[0].value, st.st_size, st.st_size, st.st_size);
|
||
LOGD("response[%s], mime_tab[%d].ext[%s]", buf, i, mime_tab[i].ext);
|
||
break;
|
||
}
|
||
}
|
||
if (mime_tab[i].ext_len == NULL)
|
||
{
|
||
snprintf(buf, 1024, "HTTP/1.0 200 OK\r\n"SERVER_STRING"Content-Type: text/html\r\n\r\n");
|
||
LOGD("response[%s]", buf);
|
||
}
|
||
send(client, buf, strlen(buf), 0);
|
||
#endif
|
||
}
|
||
|
||
/**********************************************************************/
|
||
/* Give a client a 404 not found status message. */
|
||
/**********************************************************************/
|
||
void not_found(int client)
|
||
{
|
||
char buf[1024];
|
||
|
||
sprintf(buf, "HTTP/1.0 404 NOT FOUND\r\n");
|
||
send_wrap(client, buf, strlen(buf), 0);
|
||
sprintf(buf, SERVER_STRING);
|
||
send_wrap(client, buf, strlen(buf), 0);
|
||
sprintf(buf, "Content-Type: text/html\r\n");
|
||
send_wrap(client, buf, strlen(buf), 0);
|
||
sprintf(buf, "\r\n");
|
||
send_wrap(client, buf, strlen(buf), 0);
|
||
sprintf(buf, "<HTML><TITLE>Not Found</TITLE>\r\n");
|
||
send_wrap(client, buf, strlen(buf), 0);
|
||
sprintf(buf, "<BODY><P>The server could not fulfill\r\n");
|
||
send_wrap(client, buf, strlen(buf), 0);
|
||
sprintf(buf, "your request because the resource specified\r\n");
|
||
send_wrap(client, buf, strlen(buf), 0);
|
||
sprintf(buf, "is unavailable or nonexistent.\r\n");
|
||
send_wrap(client, buf, strlen(buf), 0);
|
||
sprintf(buf, "</BODY></HTML>\r\n");
|
||
send_wrap(client, buf, strlen(buf), 0);
|
||
}
|
||
|
||
/**********************************************************************/
|
||
/* Send a regular file to the client. Use headers, and report
|
||
* errors to client if they occur.
|
||
* Parameters: a pointer to a file structure produced from the socket
|
||
* file descriptor
|
||
* the name of the file to serve */
|
||
/**********************************************************************/
|
||
void serve_file(int client, const char *filename)
|
||
{
|
||
FILE *resource = NULL;
|
||
int numchars = 1;
|
||
char buf[1024];
|
||
long long startPos = 0; /*Range<67><65><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD>ʼλ<CABC><CEBB>*/
|
||
long long endPos = 0; /*Range<67><65><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD><EFBFBD>λ<EFBFBD><CEBB>*/
|
||
int hasRange = 0;
|
||
struct stat st;
|
||
|
||
|
||
/* <20><><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD>ʼ/<2F><><EFBFBD><EFBFBD>λ<EFBFBD><CEBB> */
|
||
numchars = get_line(client, buf, sizeof(buf)); /* <20><>ȡ<EFBFBD><C8A1>һ<EFBFBD><D2BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||
while ((numchars > 0) && strcmp("\n", buf))
|
||
{
|
||
LOGD("%s\n", buf);
|
||
if (strncasecmp(buf, "Range:", 6) == 0)
|
||
{
|
||
LOGD("%s\n", buf);
|
||
hasRange = 1;
|
||
char *strRange;
|
||
strRange = strchr(&buf[7], '='); /* =1234-2345 */
|
||
strRange++; /* 1234 */
|
||
char *strNum;
|
||
strNum = strchr(strRange, '-'); /* -2345 */
|
||
*strNum = '\0';
|
||
strNum++; /* 2345 */
|
||
|
||
if(*strNum == '\r' || *strNum == '\n')/*<2A><><EFBFBD><EFBFBD>=1234-<2D><><EFBFBD><EFBFBD><EFBFBD>*/
|
||
{
|
||
LOGD("deal with =1234-");
|
||
stat(filename, &st);
|
||
startPos = atoll(strRange);
|
||
endPos = (long long)st.st_size - 1; /*st_size<7A><65><EFBFBD><EFBFBD><EFBFBD>д<EFBFBD><D0B4>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>*/
|
||
}
|
||
else
|
||
{
|
||
startPos = atoll(strRange);
|
||
endPos = atoll(strNum);
|
||
}
|
||
LOGD("the startPos = %lld\n", startPos);
|
||
LOGD("the endPos = %lld\n", endPos);
|
||
}
|
||
numchars = get_line(client, buf, sizeof(buf));
|
||
}
|
||
|
||
|
||
resource = fopen(filename, "r");
|
||
if (resource == NULL)
|
||
{
|
||
LOGD("resource is NULL\n");
|
||
not_found(client);
|
||
}
|
||
else
|
||
{
|
||
if(hasRange == 1)
|
||
{
|
||
LOGD("doing serve_partial_file\n");
|
||
/* <20><>ȡ<EFBFBD>ļ<EFBFBD><C4BC><EFBFBD>Ϣ */
|
||
stat(filename, &st);
|
||
|
||
/*<2A>ж<EFBFBD><D0B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Range<67>Ƿ<EFBFBD>Ϸ<EFBFBD>*/
|
||
if(startPos < 0 || endPos >= st.st_size) /* 0-1233/1234<33><34><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD>0<EFBFBD><30>ʼ<EFBFBD><CABC> */
|
||
{
|
||
LOGD("requested_range_not_satisfiable\n");
|
||
requested_range_not_satisfiable(client);
|
||
}
|
||
else
|
||
{
|
||
LOGD("serve_partial_file\n");
|
||
serve_partial_file(client, filename, resource, startPos, endPos, st.st_size);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
LOGD("serve_whole_file\n");
|
||
serve_whole_file(client, filename, resource);
|
||
}
|
||
}
|
||
|
||
#if 0
|
||
buf[0] = 'A'; buf[1] = '\0';
|
||
while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */
|
||
numchars = get_line(client, buf, sizeof(buf));
|
||
|
||
resource = fopen(filename, "rb");
|
||
if (resource == NULL)
|
||
not_found(client);
|
||
else
|
||
{
|
||
headers(client, filename);
|
||
cat(client, resource);
|
||
}
|
||
#endif
|
||
|
||
fclose(resource);
|
||
}
|
||
|
||
|
||
void requested_range_not_satisfiable(int sockfd)
|
||
{
|
||
char buf[1024];
|
||
|
||
sprintf(buf, "HTTP/1.1 416 Requested Range Not Satisfiable\r\n");
|
||
LOGD("%s", buf);
|
||
send_wrap(sockfd, buf, strlen(buf), 0);
|
||
|
||
sprintf(buf, SERVER_STRING);
|
||
LOGD("%s", buf);
|
||
send_wrap(sockfd, buf, strlen(buf), 0);
|
||
|
||
sprintf(buf, "Content-Type: text/html\r\n");
|
||
LOGD("%s", buf);
|
||
send_wrap(sockfd, buf, strlen(buf), 0);
|
||
|
||
sprintf(buf, "\r\n");
|
||
LOGD("%s", buf);
|
||
send_wrap(sockfd, buf, strlen(buf), 0);
|
||
|
||
sprintf(buf, "<HTML><TITLE>Requested Range Not Satisfiable</TITLE>\r\n");
|
||
LOGD("%s", buf);
|
||
send_wrap(sockfd, buf, strlen(buf), 0);
|
||
|
||
sprintf(buf, "<BODY><P>The server could not fulfill\r\n");
|
||
LOGD("%s", buf);
|
||
send_wrap(sockfd, buf, strlen(buf), 0);
|
||
|
||
sprintf(buf, "your request because the range requested\r\n");
|
||
LOGD("%s", buf);
|
||
send_wrap(sockfd, buf, strlen(buf), 0);
|
||
|
||
sprintf(buf, "is not satisfiable.\r\n");
|
||
LOGD("%s", buf);
|
||
send_wrap(sockfd, buf, strlen(buf), 0);
|
||
|
||
sprintf(buf, "</BODY></HTML>\r\n");
|
||
LOGD("%s", buf);
|
||
send_wrap(sockfd, buf, strlen(buf), 0);
|
||
}
|
||
|
||
/* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD> */
|
||
void serve_whole_file(int sockfd, const char *filename, FILE *resource)
|
||
{
|
||
headers(sockfd, filename);
|
||
cat(sockfd, resource);
|
||
}
|
||
|
||
/* <20><><EFBFBD>ز<EFBFBD><D8B2><EFBFBD><EFBFBD>ļ<EFBFBD> */
|
||
void serve_partial_file(int sockfd, const char *filename, FILE *resource, long long startPos, long long endPos, long long fileSize)
|
||
{
|
||
partial_headers(sockfd, filename, startPos, endPos);
|
||
partial_cat(sockfd, resource, startPos, endPos, fileSize);
|
||
}
|
||
|
||
/* <20><><EFBFBD>ز<EFBFBD><D8B2><EFBFBD><EFBFBD>ļ<EFBFBD>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD>Ӧͷ<D3A6><CDB7> */
|
||
void partial_headers(int sockfd, const char *filename, long long startPos, long long endPos)
|
||
{
|
||
char buf[1024];
|
||
char contentType[128];
|
||
char suffix[16];
|
||
char *pos = NULL;
|
||
int i;
|
||
//long long resstartPos = startPos;
|
||
//long long resendPos = endPo
|
||
struct stat st;
|
||
(void)filename; /* could use filename to determine file type */
|
||
|
||
|
||
/* <20><><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD><EFBFBD> */
|
||
pos = (char*)strrchr(filename, '.');
|
||
if(pos != NULL)
|
||
{
|
||
pos++;
|
||
for(i = 0; mime_tab[i].ext_len != 0; i++)
|
||
{
|
||
if(strcmp(pos, mime_tab[i].ext) == 0)
|
||
{
|
||
strcpy(contentType, mime_tab[i].value);
|
||
break;
|
||
}
|
||
}
|
||
if(mime_tab[i].ext_len == 0)
|
||
{
|
||
strcpy(contentType, "application/octet-stream");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
strcpy(contentType, "application/octet-stream");
|
||
}
|
||
|
||
/* <20><>ȡ<EFBFBD>ļ<EFBFBD><C4BC><EFBFBD>Ϣ */
|
||
stat(filename, &st);
|
||
|
||
strcpy(buf, "HTTP/1.1 206 Partial Content\r\n");
|
||
LOGD("%s", buf);
|
||
send_wrap(sockfd, buf, strlen(buf), 0);
|
||
|
||
strcpy(buf, SERVER_STRING);
|
||
LOGD("%s", buf);
|
||
send_wrap(sockfd, buf, strlen(buf), 0);
|
||
|
||
sprintf(buf, "Content-Type: %s\r\n", contentType);
|
||
LOGD("%s", buf);
|
||
send_wrap(sockfd, buf, strlen(buf), 0);
|
||
|
||
sprintf(buf, "Content-Length: %lld\r\n", endPos-startPos+1);//<2F>˴<EFBFBD><CBB4><EFBFBD>Ӧ<EFBFBD><D3A6>Ϊ<EFBFBD>ܴ<EFBFBD>Сst.st_size<7A><65><EFBFBD><EFBFBD>Ӧ<EFBFBD><D3A6>Ϊʵ<CEAA>ʷ<EFBFBD><CAB7>صij<D8B5><C4B3><EFBFBD>st.st_size);
|
||
LOGD("%s", buf);
|
||
printf("the st.st_size is %lld\n", st.st_size);
|
||
send_wrap(sockfd, buf, strlen(buf), 0);
|
||
|
||
sprintf(buf, "Content-Range: bytes %lld-%lld/%lld\r\n", startPos, endPos, st.st_size);
|
||
LOGD("%s", buf);
|
||
send_wrap(sockfd, buf, strlen(buf), 0);
|
||
|
||
sprintf(buf, "Connection: Keep-Alive\r\n");
|
||
LOGD("%s", buf);
|
||
send_wrap(sockfd, buf, strlen(buf), 0);
|
||
|
||
strcpy(buf, "\r\n");
|
||
send_wrap(sockfd, buf, strlen(buf), 0);
|
||
}
|
||
|
||
/* <20><><EFBFBD>ز<EFBFBD><D8B2><EFBFBD><EFBFBD><EFBFBD>Ӧʱ<D3A6><CAB1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||
void partial_cat(int sockfd, FILE *resource, long long startPos, long long endPos, long long fileSize)
|
||
{
|
||
char buf[4096];
|
||
int i = 0;
|
||
int readlen;
|
||
int sendret;
|
||
long long sendlength = endPos - startPos + 1; /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݳ<EFBFBD><DDB3><EFBFBD> */
|
||
|
||
/*<2A>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD><EFBFBD>2G<32><47>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD>м<EFBFBD><D0BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>*/
|
||
if(startPos <= 2147483647)
|
||
{ /*˳<><CBB3>λ*/
|
||
if(fseek(resource, startPos, SEEK_SET) != 0)
|
||
{
|
||
LOGD("[partial_cat] fseek error!\n");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/*<2A><><EFBFBD><EFBFBD>λ*/
|
||
long long rStartPos = (fileSize - 1) - startPos;
|
||
if(fseek(resource, rStartPos, SEEK_END) != 0)
|
||
{
|
||
LOGD("[partial_cat] reverse fseek error!\n");
|
||
}
|
||
|
||
}
|
||
|
||
|
||
while((readlen = fread(buf, sizeof(char), 4096, resource)))
|
||
{
|
||
//printf("i = %d, readlen = %d\n", ++i, readlen);
|
||
if(sendlength < readlen)
|
||
{
|
||
//LOGD("i = %d, sendlength = %d\n", ++i, sendlength);
|
||
|
||
sendret = send_wrap(sockfd, buf, sendlength, 0);
|
||
if(sendret < 0)
|
||
{
|
||
LOGD("[partial_cat] send error! [%s]\n", strerror(errno));
|
||
return;
|
||
}
|
||
sendlength -= sendlength;
|
||
}
|
||
else
|
||
{
|
||
//LOGD("i = %d, readlen = %d\n", ++i, readlen);
|
||
|
||
sendret = send_wrap(sockfd, buf, readlen, 0);
|
||
if(sendret < 0)
|
||
{
|
||
LOGD("[partial_cat] send error! [%s]\n", strerror(errno));
|
||
return;
|
||
}
|
||
++i;
|
||
|
||
//if(i%500 == 0)
|
||
//{
|
||
// struct timeval t_timeval;
|
||
// t_timeval.tv_sec = DOWNLOAD_SPEED_SEC;
|
||
// t_timeval.tv_usec = DOWNLOAD_SPEED_MICRO_SEC;
|
||
usleep(50000);
|
||
//select(0, NULL, NULL, NULL, &t_timeval);
|
||
//}
|
||
|
||
sendlength -= readlen;
|
||
}
|
||
if(sendlength == 0)
|
||
{
|
||
/* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||
//LOGD("sendlength == 0\n");
|
||
break;
|
||
}
|
||
}
|
||
/* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݻ<EFBFBD><DDBB>ߴ<EFBFBD><DFB4><EFBFBD> */
|
||
if(ferror(resource))
|
||
{
|
||
LOGD("[partial_cat] end before eof\n");
|
||
return;
|
||
}
|
||
|
||
LOGD("send partial file finish");
|
||
|
||
}
|
||
|
||
|
||
/**********************************************************************/
|
||
/* This function starts the process of listening for web connections
|
||
* on a specified port. If the port is 0, then dynamically allocate a
|
||
* port and modify the original port variable to reflect the actual
|
||
* port.
|
||
* Parameters: pointer to variable containing the port to connect on
|
||
* Returns: the socket */
|
||
/**********************************************************************/
|
||
int startup(u_short *port)
|
||
{
|
||
int httpd = 0;
|
||
struct sockaddr_in name;
|
||
|
||
httpd = socket(PF_INET, SOCK_STREAM, 0);
|
||
if (httpd == -1)
|
||
error_die("socket");
|
||
memset(&name, 0, sizeof(name));
|
||
name.sin_family = AF_INET;
|
||
name.sin_port = htons(*port);
|
||
name.sin_addr.s_addr = htonl(INADDR_ANY);
|
||
if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0)
|
||
error_die("bind");
|
||
if (*port == 0) /* if dynamically allocating a port */
|
||
{
|
||
int namelen = sizeof(name);
|
||
if (getsockname(httpd, (struct sockaddr *)&name, (socklen_t*)&namelen) == -1)
|
||
error_die("getsockname");
|
||
*port = ntohs(name.sin_port);
|
||
}
|
||
if (listen(httpd, 10) < 0)
|
||
error_die("listen");
|
||
return(httpd);
|
||
}
|
||
|
||
/**********************************************************************/
|
||
/* Inform the client that the requested web method has not been
|
||
* implemented.
|
||
* Parameter: the client socket */
|
||
/**********************************************************************/
|
||
void unimplemented(int client)
|
||
{
|
||
char buf[1024];
|
||
|
||
sprintf(buf, "HTTP/1.0 501 Method Not Implemented\r\n");
|
||
send_wrap(client, buf, strlen(buf), 0);
|
||
sprintf(buf, SERVER_STRING);
|
||
send_wrap(client, buf, strlen(buf), 0);
|
||
sprintf(buf, "Content-Type: text/html\r\n");
|
||
send_wrap(client, buf, strlen(buf), 0);
|
||
sprintf(buf, "\r\n");
|
||
send_wrap(client, buf, strlen(buf), 0);
|
||
sprintf(buf, "<HTML><HEAD><TITLE>Method Not Implemented\r\n");
|
||
send_wrap(client, buf, strlen(buf), 0);
|
||
sprintf(buf, "</TITLE></HEAD>\r\n");
|
||
send_wrap(client, buf, strlen(buf), 0);
|
||
sprintf(buf, "<BODY><P>HTTP request method not supported.\r\n");
|
||
send_wrap(client, buf, strlen(buf), 0);
|
||
sprintf(buf, "</BODY></HTML>\r\n");
|
||
send_wrap(client, buf, strlen(buf), 0);
|
||
}
|
||
|
||
/**********************************************************************/
|
||
void* acceptThread(void *argv)
|
||
{
|
||
int server_sock = -1;
|
||
|
||
#ifdef SDV
|
||
u_short port = 80;
|
||
#else
|
||
u_short port = 8082;
|
||
#endif
|
||
int client_sock = -1;
|
||
struct sockaddr_in client_name;
|
||
int client_name_len = sizeof(client_name);
|
||
pthread_t newthread;
|
||
|
||
server_sock = startup(&port);
|
||
printf("httpd running on port %d\n", port);
|
||
startFlag = 1;
|
||
while(startFlag == 1)
|
||
{
|
||
fd_set rfd;
|
||
FD_ZERO(&rfd);
|
||
FD_SET(server_sock, &rfd);
|
||
struct timeval to = {1, 0};
|
||
int selectResult = select(server_sock+1, &rfd, NULL, NULL, &to);
|
||
if (selectResult > 0)
|
||
{
|
||
client_sock = accept(server_sock, (struct sockaddr *)&client_name, (socklen_t*)&client_name_len);
|
||
if (client_sock == -1)
|
||
{
|
||
perror("accept");
|
||
continue;
|
||
}
|
||
LOGD("accept success, client_sock:%d", client_sock);
|
||
/* accept_request(client_sock); */
|
||
int *client_sock_ptr = new int;
|
||
*client_sock_ptr = client_sock;
|
||
// if (pthread_create(&newthread, NULL, accept_request, (void*)client_sock_ptr) != 0)
|
||
// {
|
||
// perror("pthread_create");
|
||
// continue;
|
||
// }
|
||
// pthread_detach(newthread);
|
||
accept_request(client_sock_ptr);
|
||
}
|
||
else if(selectResult < 0)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
startFlag = 0;
|
||
close(server_sock);
|
||
|
||
return(0);
|
||
}
|
||
|
||
int startHttpd(httpdCallback *callback)
|
||
{
|
||
/*<2A><><EFBFBD><EFBFBD>SIGPIPE<50>ź<EFBFBD>*/
|
||
signal(SIGPIPE, SIG_IGN);
|
||
printf("block SIGPIPE\n");
|
||
if (startFlag == 1) {
|
||
return -1;
|
||
}
|
||
|
||
if(callback)
|
||
{
|
||
gHttpdCallback.httpdDataCallback = callback->httpdDataCallback;
|
||
gHttpdCallback.httpdStatCallback = callback->httpdStatCallback;
|
||
gHttpdCallback.handler = callback->handler;
|
||
}
|
||
if(pthread_create(&acceptThreadId, NULL, acceptThread, NULL) != 0) {
|
||
perror("pthread_create");
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
int stopHttpd()
|
||
{
|
||
startFlag = 0;
|
||
pthread_join(acceptThreadId, NULL);
|
||
gHttpdCallback.httpdDataCallback = NULL;
|
||
gHttpdCallback.httpdStatCallback = NULL;
|
||
gHttpdCallback.handler = NULL;
|
||
return 0;
|
||
}
|
||
|