Giter VIP home page Giter VIP logo

tinyhttpd's People

Contributors

ezlippi avatar huntinux avatar kingofctrl avatar mekayangyi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tinyhttpd's Issues

你要发就发1999年的源码别乱改好不好

在execute_cgi()函数中,你在fork()之后 调用sprintf(buf, "HTTP/1.0 200 OK\r\n");send(client, buf, strlen(buf), 0);这会导致这个信息发了两次,发代码之前都不用一下的吗?

execute_cgi中父子进程建立管道的作用

相关代码如下:
` if (pid == 0) /* child: CGI script */
{
char meth_env[255];
char query_env[255];
char length_env[255];

    dup2(cgi_output[1], STDOUT);
    dup2(cgi_input[0], STDIN);
    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, 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(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);
}`

这段程序功能大致了解:子进程执行cgi脚本,父进程从client的post请求中读取数据,如果output中有输出,则返回给client。

不清楚的是:input和output管道作用是什么?子进程为何使用dup复制output[1]到标准输出,复制input[0]到标准输入?

无道理的猜测:父进程将post中请求数据传入cgi脚本,子进程执行完成后,若有数据,则写入output[0]中,父进程从中读取。

哪位大佬路过解答一波~

浏览器无法显示网页

按照注释修改代码,编译,运行./httpd后,浏览器中输入127.0.0.1:4000,一直在加载无法显示index.html网页。

image

execute_cgi中设置环境变量的作用是什么?

初学,
源码部门如下

char meth_env[255];
char query_env[255];
char length_env[255];
 ,,,
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);
}

请问这些环境变量有什么作用?

accept_request函数BUG

void accept_request(void *arg)
{
    int client = (intptr_t)arg;
    ......
}

intptr_t转换后的client不是传入的值。
系统版本:Ubuntu 16.04 LTS

在虚拟机运行httpd,然后在windows浏览器中访问

自己在运行项目时遇到了一些问题,现在已经解决。

问题
成功运行httpd后,可以通过命令curl -v localhost:4000/index.htmlcurl -v localhost:4000/color.cgi从服务端获得回应,但无法通过windows浏览器访问服务端。
解决
1、设置防火墙开放相应端口firewall-cmd --add-port=4000/tcp。这里修改了代码,将端口固定为4000而不是让系统随机分配。
2、使用whereis perl命令获得perl程序的地址,然后vim htdocs/color.cgi,将第一行的perl地址改为自己查到的路径。

在windows浏览器中输入(自己的虚拟机ip):4000,可成功访问index.html界面,输入颜色可获得color.cgi的运行结果。

执行make all报错

执行make all报错了

gcc -g -W -Wall -lpthread  -o httpd httpd.c
httpd.c: In function ‘execute_cgi’:
httpd.c:281:9: warning: null argument where non-null required (argument 2) [-Wnonnull]
         execl(path, NULL);
         ^~~~~
httpd.c:281:9: warning: not enough variable arguments to fit a sentinel [-Wformat=]
/tmp/cck2ie6l.o:在函数‘main’中:
/home/zhujianchen/work/git/Tinyhttpd/httpd.c:503:对‘pthread_create’未定义的引用
collect2: error: ld returned 1 exit status
make: *** [Makefile:4:httpd] 错误 1

您好,请问这是啥问题了

有缺陷

在accept_request函数中,sprintf(path, "htdocs%s", url);明显是有问题的,htdocs后面没有‘/’,连接url会有问题。

cat函数没有将最后一行发送过去

`void cat(int client, FILE *resource)
{
char buf[1024];

fgets(buf, sizeof(buf), resource);
while (!feof(resource))
{
    send(client, buf, strlen(buf), 0);
    fgets(buf, sizeof(buf), resource);//文件的最后一行并没有发送出去
}

}`

线程之间的竞争

在main函数中,传递给线程的参数client_sock是一个local variable,会产生竞争吗?
if (pthread_create(&newthread , NULL, (void *)accept_request, (void *)&client_sock) != 0) perror("pthread_create");

比如连接1对应的线程运行前,连接2把client_sock修改了。
是否应该在堆上分配

startup函数中,getsockname检查、请求分配端口为何要在bind后?

if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0)
error_die("bind");
if (port == 0) / if dynamically allocating a port */
{
socklen_t namelen = sizeof(name);
if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1)
error_die("getsockname");
*port = ntohs(name.sin_port);
}

为什么不在bind前判断端口是否为0,为0就调用getsockname让系统自动分配。
现在在bind后,如果bind的端口为0,bind失败、分配了也没用,代码里也没有重新bind。

mac 怎么安装perl的cgi

perl -Tw color.cgi
Can't locate CGI.pm in @inc (you may need to install the CGI module) (@inc contains: /usr/local/Cellar/perl/5.34.0/lib/perl5/site_perl/5.34.0/darwin-thread-multi-2level /usr/local/Cellar/perl/5.34.0/lib/perl5/site_perl/5.34.0 /usr/local/Cellar/perl/5.34.0/lib/perl5/5.34.0/darwin-thread-multi-2level /usr/local/Cellar/perl/5.34.0/lib/perl5/5.34.0 /usr/local/lib/perl5/site_perl/5.34.0) at color.cgi line 4.perimeter81 persepolis-download-manager
BEGIN failed--compilation aborted at color.cgi line 4.

好像找不到cgi相关的东西,导致cgi脚本报错

如果在buf在recv中没有收到任何消息,即buf[0] = '\n'

numchars = get_line(client, buf, sizeof(buf));
i = 0; j = 0;
while (!ISspace(buf[i]) && (i < sizeof(method) - 1))
{
method[i] = buf[i];
i++;
}
....
if (strcasecmp(method, "GET") && strcasecmp(method, "POST"))
{
unimplemented(client);
return;
}
此时直接return会发生管道破裂,内核会给进程发送signal SIGPIPE, Broken pipe消息,进程终结。所以在return之前要关闭accept函数的fd

accept_request中直接将指针赋给client

main函数中 ”accept_request(&client_sock);“ 将地址传入。
accept_request函数中 ”int client = (intptr_t)arg;“ 将地址赋给client,将导致recv读不到数据。

参数传递

pthread_create(&newthread , NULL, (void *)accept_request, (void *)&client_sock ), 这里有问题吧, 你的client_sock地址指向的内容在下一次请求接收时,会发生变化.

输入127.0.0.1后浏览器一直加载以及输入颜色后没有显示的解决方案

首先是网上说的将index.html的访问权限改为不可执行,参考第一个评论https://zhuanlan.zhihu.com/p/105656761
但是还是浏览器运行后一直显示加载,我使用gdb调试后发现,clint_sock本来是4但是在传入accept_request之后,赋给client的值是-8764
image
此时我注意到了intptr_t并查阅了相关的资料,发现这个类型的大小等于机器的位数,由此保证void转换成立,但是我编写了一个程序发现这个转化后的值等于指针的值而不是client的值,因此我将int client=(intptr_t)arg;改为了int client=(int*)arg;使得浏览器可以访问,但是这个用法不知道是哪里出现了问题。
image
最后输入颜色没有显示的问题是由于perl路径设置错误,输入which perl获得perl的路径,在check.cgi和color.cgi中的第一行的路径改为自己的就可以了。
希望可以帮到一些和我遇到同样问题的人

waitpid函数为何放在最后?不应该放在send前吗?

	//waitpid(pid, &status, 0);我觉得应该放在这里啊
	 
    while (read(cgi_output[0], &c, 1) > 0)
        send(client, &c, 1, 0);

    close(cgi_output[0]);
    close(cgi_input[1]);

	/*此处存在疑问,不应该将waitpid放在前面吗?以确保子进程先运行*/
    waitpid(pid, &status, 0);

waitpid放在最后如何保证子进程结束后可以send给client?

cat函数在读取文件的字符数小于fget的单次读取数时出现的问题

问题

void cat(int client, FILE *resource)
{
    char buf[1024];

    fgets(buf, sizeof(buf), resource);
    while (!feof(resource))
    {
        send(client, buf, strlen(buf), 0);
        fgets(buf, sizeof(buf), resource);
    }
}

cat 函数在读取长度小于 1024 的文件时,在第一次 fgets 后 resourse 就已经被设为 eof 了,因此也无法进入循环 send 了。

测试

test.txt

hello world

a.c

#include <stdio.h>

#define MAXLEN 20

int main() {
    FILE *fp;
    char str[MAXLEN];

    fp = fopen("test.txt", "r");
    if (fp == NULL) {
        perror("ERROR OPEN FILE");
        return -1;
    }

    if (fgets(str, sizeof(str), fp) != NULL) {
        puts(str);
    }

    if (feof(fp)) {
        puts("eof");
    } else {
        puts("no eof");
    }
    fclose(fp);

    return 0;
}

执行结果

image

一定要discard headers么?

sprintf(path, "htdocs%s", url);
if (path[strlen(path) - 1] == '/')
    strcat(path, "index.html");
if (stat(path, &st) == -1) {
    while ((numchars > 0) && strcmp("\n", buf))  /* read & discard headers */
        numchars = get_line(client, buf, sizeof(buf));
    not_found(client);
}

这里discard headers是一定需要的么?

运行./httpd后在地址栏输入127.0.0.1:4000没有响应

下面的是我在主机Ubuntu Kylin 16.04 LTS上编译信息,我已经按照说明注释了该注释的部分和需要去除注释的部分,但是依然有warning,而且在地址栏输入127.0.0.1:4000时,没有那个index响应。

我是一个初学者,所以还不理解源代码。我根据Google的结果,参照qiyeboy的Tinyhttpd
execl(path, NULL) 修改为 execl(path, path, NULL),虽然编译httpd.c时没有warming了,但是输入127.0.0.1:4000依然没有响应。还望指点一下。

gcc -g -W -Wall -lpthread  -o httpd httpd.c

httpd.c: In function ‘execute_cgi’:
httpd.c:282:9: warning: null argument where non-null required (argument 2) [-Wnonnull]
         execl(path, NULL);
         ^
httpd.c:282:9: warning: not enough variable arguments to fit a sentinel [-Wformat=]

gcc -W -Wall -o client simpleclient.c
simpleclient.c: In function ‘main’:
simpleclient.c:26:9: warning: implicit declaration of function ‘exit’ [-Wimplicit-function-declaration]
         exit(1);
         ^
simpleclient.c:26:9: warning: incompatible implicit declaration of built-in function ‘exit’
simpleclient.c:26:9: note: include ‘<stdlib.h>’ or provide a declaration of ‘exit’
simpleclient.c:32:5: warning: incompatible implicit declaration of built-in function ‘exit’
     exit(0);
     ^
simpleclient.c:32:5: note: include ‘<stdlib.h>’ or provide a declaration of ‘exit’
simpleclient.c:8:14: warning: unused parameter ‘argc’ [-Wunused-parameter]
 int main(int argc, char *argv[])
              ^
simpleclient.c:8:26: warning: unused parameter ‘argv’ [-Wunused-parameter]
 int main(int argc, char *argv[])
                          ^

内存不断变大

我本地是MAC ,启动tinyhttpd后,用postman或者浏览器不断发送请求,用activity monitor观察内存不断变大,如果持续下去内存岂不是要爆掉,如何优化呢?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.