This time, we need to port a web service in SylixOS and support standard cgi. So I collected a very small web service - tinyhttpd on the Internet.
How small is the web service of this single C file? The source c file is only 16kb. There are a total of more than a dozen functions to realize the http service. It is the best starting program to learn the principle of http server.
Don't say much. Start migration directly
First, create an APP program with arbitrary name, and then copy the tinyhttpd source code into the main file to complete the compilation. Because the original code basically has no compilation errors.
It seems that this article is over. The program can run and listen to port 4001
Question 1: modify the listening IP
There is no problem with program monitoring, but it can't be accessed. Finally, it is found that the listening IP needs to be manually specified, and the modified code is as follows
Question 2, the page is not rendered
Compared with normal web services, it is found that tinyhttpd is too simple, which leads to the return type of header, which is not detailed enough. The return types of js and css are wrong. Here, you can add types according to future requirements.
void headers(int client, const char *filename) { char buf[1024]; (void)filename; /* could use filename to determine file type */ const char *pFile; pFile = strrchr(filename, '.'); // Judge the input file name and the final output Location of strcpy(buf, "HTTP/1.0 200 OK\r\n"); send(client, buf, strlen(buf), 0); strcpy(buf, SERVER_STRING); send(client, buf, strlen(buf), 0); //sunjinxiugai ,zengjia if (strcmp(pFile, ".css") == 0) { sprintf(buf, "Content-Type: text/css\r\n"); } else if (strcmp(pFile, ".js") == 0) { sprintf(buf, "Content-Type: application/x-javascript\r\n"); } else { sprintf(buf, "Content-Type: text/html\r\n"); } send(client, buf, strlen(buf), 0); strcpy(buf, "\r\n"); send(client, buf, strlen(buf), 0); }
Problem 3: large file loading failed
There is still a problem with page rendering. As a result, it is found that the memory of the function to read and send the file is not large enough, and the file is not sent completely, because fgets comes according to the line, but there are many js and css compressed, which itself is a long line of data. Therefore, changing memory reading to ensure data integrity.
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); // } int rc; unsigned char buf[1024]; while( (rc = fread(buf,sizeof(unsigned char), 1024,resource)) != 0 ) { send(client, buf, rc, 0); } }
Problem 4, cgi cannot call
The same code has no problem in testing in other linux systems. After changing to SylixOS, there is no problem with cgi compilation, but when cgi is requested, the cgi program will not be executed. Debugging found that the problem appears here
There is an ominous feeling that there is a problem with the fork function. After querying the document, it is found that sylixOS does not support the fork function
Through searching for information on the Internet, I found the article of a great God https://my.oschina.net/u/3022273/blog/832350 . Borrow a picture of the town building
Split the child process in the original code into a child process, and pass the original parameters that can be accessed directly to the child process through the parameters. The codes of the child process and the parent process are replaced as follows. The parameters are passed in a fool's string, because it is found that only so many parameters can be passed. If only pipes are passed, official documents can also be used, However, there are too many parameters here, which is really not enough. The following is the part of replacing parent-child processes.
{ char *cmd[9]; char resp[1024]; int rsplen=0; for (i = 0; i < 8; i++) { cmd[i]=malloc(1024); } cmd[8]=NULL; sprintf(cmd[0],"%d",cgi_output[0]); sprintf(cmd[1],"%d",cgi_output[1]); sprintf(cmd[2],"%d",cgi_input[0]); sprintf(cmd[3],"%d",cgi_input[1]); strcpy(cmd[4],method); strcpy(cmd[5],query_string); strcpy(cmd[6],path); sprintf(cmd[7],"%d",content_length); posix_spawnp(&pid, "./jqr_cgi_child", NULL, NULL, cmd, NULL); /* 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); } } memset(resp,0,1024); rsplen=read(cgi_output[0], resp, 1024); if(rsplen>=1024) { rsplen=1023; } resp[rsplen]='\0'; send(client, resp, strlen(resp), 0); close(cgi_output[0]); close(cgi_input[1]); Lw_Time_MSleep(100); for (i = 0; i < 8; i++) { free(cmd[i]); } waitpid(pid, &status, 0); }
The child process code is as follows. Similarly, take the parameters from argv and use them again.
{ int cgi_output[2]; int cgi_input[2]; char method[1024]; char query_string[1024]; char path[1024]; int content_length; char meth_env[255]; char query_env[255]; char length_env[255]; cgi_output[0]=atoi(argv[0]); cgi_output[1]=atoi(argv[1]); cgi_input[0]=atoi(argv[2]); cgi_input[1]=atoi(argv[3]); strcpy(method,argv[4]); strcpy(query_string,argv[5]); sprintf(path,"./%s",argv[6]); content_length= atoi(argv[7]); 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, "main.cgi","test", (char *)0); exit(0); return (0); }
As for cgi program, you can choose any one. c language is the best, and it is supported by all platforms.