【发布时间】:2019-09-26 08:40:17
【问题描述】:
我已经制作了一个自定义 HTTP 服务器,它提供一个带有一些文本的 HTML 页面、一个用于输入命令的输入文本框和一个提交按钮。收到表单提交后,服务器应该运行给定的命令并提供包含结果的响应。
我想不通的问题是如何获取表单提交请求的 URL 以便解析出要运行的命令。正如目前实现的那样,服务器在 localhost:3838 上运行,当客户端浏览到该 URL 时,服务器会正确响应表单。当用户在文本框中输入(比如说)命令ls 并单击“运行”提交按钮时,会向localhost:3838/run?command=ls 发出请求。如何在服务器中获取该URL,以便解析并执行命令?
这是当前的服务器代码:
CwebServer.c
#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>
#define PORT "3838" //port being connected to
#define MAXLEN 800
#define BACKLOG 10 //number of pending connections to be held in queue
//format of html page
char header []=
"HTTP/1.1 200 Ok\r\n"
"Content-Type: text/html; charset=UTF-8\r\n\r\n"
"<!DOCTYPE html>\r\n"
"<html>\n"
"<head>\n"
"<title>Web-Based Remote Command Server</title>\r\n"
"</head>\n"
"<body>\n\n";
char input []=
"<form action= \"/run\" method= \"GET\"> \n"
"Command: <input type=\"text\" size=\"100\" name=\"command\">\n"
"<input type=\"submit\" value=\"Run\">\n"
"</form>";
char output []=
"<p>Command that was run and testing this:</p>\n"
"<pre>Your server will include the command that was just run here.</pre>\n\n"
"<p>Standard Output:</p>\n""<pre>Your server will include the stdout results here.</pre>\n\n"
"<p>Standard Error:</p>\n"
"<pre>Your server will include the stderr results here.</pre>\r\n\r\n"
"</body>\r\n""</html>\r\n";
char *buff = header;
void sigchld_handler(int s)
{
(void)s; // quiet unused variable warning
// waitpid() might overwrite errno, so we save and restore it:
int saved_errno = errno;
while(waitpid(-1, NULL, WNOHANG) > 0);
errno = saved_errno;
}
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main (void){
int sockfd;
int new_fd;
struct addrinfo hints;
struct addrinfo *serverinfo;
struct addrinfo *p;
struct sockaddr_storage client_addr;
socklen_t addrsize;
struct sigaction sa;
int yes = 1;
char s[INET6_ADDRSTRLEN];
int status;
memset(&hints, 0, sizeof hints); //makes struct empty
hints.ai_family = AF_UNSPEC; //IPv4 or v6
hints.ai_socktype = SOCK_STREAM; //TCP type need
hints.ai_flags = AI_PASSIVE; //Fill in IP for us
//if can't get address info print error
if((status = getaddrinfo(NULL, PORT, &hints, &serverinfo)) != 0){
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
return 1;
}
for(p = serverinfo; 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;
}
freeaddrinfo(serverinfo);
if(p == NULL){
fprintf(stderr, "server: failed to bind\n");
exit(1);
}
if(listen(sockfd, BACKLOG) == -1){
perror("listen");
exit(1);
}
sa.sa_handler = sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
perror("sigaction");
exit(1);
}
printf("server: waiting for connections....\n");
while(1){
addrsize = sizeof client_addr;
new_fd = accept(sockfd, (struct sockaddr *)&client_addr, &addrsize);
if(new_fd == -1){
perror("Did not accept");
continue;
}
inet_ntop(client_addr.ss_family, get_in_addr((struct sockaddr *)&client_addr), s, sizeof s);
printf("server: got connection from %s\n", s);
if(!fork()){
close(sockfd);
int bufsize = 1024;
char *buffer = malloc(bufsize);
send(new_fd, header, bufsize, 0);
//write(new_fd, "HTTP/1.1 200 OK\n", 16);
//write(new_fd, "Content-length: 46\n", 19);
//write(new_fd, "Content-type: text/html\n\n", 25);
//write(new_fd, "<html><head>\n<head>\n<title>The CAvengers Web Page</title>\n</head>\n</html>", 46);
if(send(new_fd, buffer, MAXLEN, 0) == -1)
perror("send");
close(new_fd);
exit(0);
}
close(new_fd);
}
return 0;
}
【问题讨论】:
-
libcurl 提供了一个 API 用于发出 HTTP 请求并接收相应的响应。相反,您可以直接实现您所需要的,但是如果您要询问详细信息,那么这个问题就太宽泛了。
-
我要求在没有 libcurl 的情况下执行此操作。我真正要问的是如何将该 URL 拉出并将其放入变量中以便能够对其进行标记以供使用。
-
很抱歉,我很难按照您的要求进行操作。 libcurl 提供了一个 HTTP 客户端。我知道您不想使用它,但是由于您要求使用 libcurl 的替代方案,因此答案是“实现您自己的 HTTP 客户端”的一些变体。另一方面,您已经提供了 HTTP 服务器 的代码。目前还不清楚这些因素是如何影响的。总体而言,您要解析什么,在哪里解析?服务器上的 HTTP 请求(与 libcurl 无关)?客户端上的 HTTP 响应?还有什么?
-
对于这个令人困惑的问题,我深表歉意,我想我只是在脑海中说得通,但也许这是我的问题的一部分。我正在尝试对严格在 localhost:3838 上运行的 HTTP 服务器执行什么操作,并且当运行时有一个文本框,当您在此文本框中键入类似 ls 的命令时,它会将您带到
localhost:3838/run?command=ls的 URL 我是什么问的是我如何才能将该 URL 拉出并将其放入变量中。其余的我可以自己解决但本质上我将把ls部分拉出并将其插入 execvp 然后将该输出插入服务器以显示 -
缺乏清晰度似乎至少部分是因为您没有清楚地区分服务器的角色和客户端的角色。我会看看我能做些什么来解决这个问题。