cgi 不能访问外部资源,cannot create directory ==>超级安全的selinux

本文探讨了如何解决SELinux权限限制导致无法创建目录的问题,通过修改配置文件,实现系统的正常运行。
今天,做一个监控脚本,但总是不能成功创建目录,

经过认真检查,权限没有问题。

经过反复测试,/tmp目录下有读写操作,其余均不能。

反复思考,最终定位在selinux

执行:getenforce
enforcing

执行:cat /etc/selinux/config
SELINUX=enforcing
SELINUXTYPE=targeted
SETLOCALDEFS=0

找到问题。

修改cat /etc/selinux/config
SELINUX=disabled

执行:setenforce 0
成功。
#include <stdio.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <netinet/tcp.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 <dirent.h> #include <time.h> #define ISspace(x) isspace((int)(x)) #define SERVER_STRING “Server: jdbhttpd/0.2.0\r\n” // 函数声明 void* accept_request(void *); 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 *, const char *); void not_found(int); void serve_file(int, const char *); int startup(unsigned short *); void unimplemented(int); void forbidden(int); void serve_directory(int, const char *); void url_decode(char *, const char *); const char *get_mime_type(const char *); void log_request(const char *, const char *, int); void set_socket_timeout(int, int); // / 处理客户端请求 / // void* accept_request(void *pclient) { int client = (int)pclient; free(pclient); set_socket_timeout(client, 2); char buf[65536]; int numchars; char method[255]; char url[255]; char path[1024]; // 增加路径长度 char decoded_url[1024]; // 存储解码后的URL size_t i, j; struct stat st; int cgi = 0; char *query_string = NULL; // 获取请求的第一行 numchars = get_line(client, buf, sizeof(buf)); i = 0; j = 0; while (!ISspace(buf[j]) && (i < sizeof(method) - 1)) { method[i] = buf[j]; i++; j++; } method[i] = '\0'; // 只支持GET和POST方法 if (strcasecmp(method, "GET") && strcasecmp(method, "POST")) { unimplemented(client); return NULL; } // POST请求启用CGI /*if (strcasecmp(method, "POST") == 0) cgi = 1; */ // 读取URL 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'; // URL解码 url_decode(decoded_url, url); // 记录请求日志 log_request(method, decoded_url, client); // 处理GET请求 if (strcasecmp(method, "GET") == 0) { query_string = decoded_url; while ((*query_string != '?') && (*query_string != '\0')) query_string++; if (*query_string == '?') { cgi = 1; *query_string = '\0'; query_string++; } } // 构建文件路径(使用当前目录) sprintf(path, "web%s", decoded_url); // 防止路径遍历攻击 if (strstr(path, "..")) { forbidden(client); close(client); return NULL; } // 处理目录请求 if (path[strlen(path) - 1] == '/') strcat(path, "Index.html"); // 检查文件/目录是否存在 if (stat(path, &st) == -1) { // 检查是否存在.html扩展名文件 char alt_path[1024]; sprintf(alt_path, "%s.html", path); if (stat(alt_path, &st) == 0) { strcpy(path, alt_path); } else { while ((numchars > 0) && strcmp("\n", buf)) numchars = get_line(client, buf, sizeof(buf)); not_found(client); close(client); return NULL; } } // 如果是目录 if ((st.st_mode & S_IFMT) == S_IFDIR) { // 检查目录中是否有index.html char index_path[1024]; sprintf(index_path, "%s/Index.html", path); if (stat(index_path, &st) == 0) { strcpy(path, index_path); } else { // 显示目录列表 serve_directory(client, path); close(client); return NULL; } } // 检查执行权限 if ((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH)) cgi = 1; // 处理静态文件或CGI if (!cgi) serve_file(client, path); else execute_cgi(client, path, method, query_string); close(client); return NULL; } // / URL解码 / // void url_decode(char *dest, const char *src) { char *p = dest; while (*src) { if (*src == ‘%’) { if (src[1] && src[2]) { char hex[3] = {src[1], src[2], ‘\0’}; *p++ = (char)strtol(hex, NULL, 16); src += 3; } else { *p++ = *src++; } } else if (*src == ‘+’) { *p++ = ’ '; src++; } else { *p++ = *src++; } } *p = ‘\0’; } // / 获取MIME类型 / // const char *get_mime_type(const char *filename) { const char *dot = strrchr(filename, ‘.’); if (!dot) return “text/plain”; if (strcasecmp(dot, ".html") == 0 || strcasecmp(dot, ".htm") == 0) return "text/html"; if (strcasecmp(dot, ".css") == 0) return "text/css"; if (strcasecmp(dot, ".js") == 0) return "application/javascript"; if (strcasecmp(dot, ".jpg") == 0 || strcasecmp(dot, ".jpeg") == 0) return "image/jpeg"; if (strcasecmp(dot, ".png") == 0) return "image/png"; if (strcasecmp(dot, ".gif") == 0) return "image/gif"; if (strcasecmp(dot, ".json") == 0) return "application/json"; if (strcasecmp(dot, ".ico") == 0) return "image/x-icon"; return "text/plain"; } // / 记录请求日志 / // void log_request(const char *method, const char *url, int client) { time_t now = time(NULL); struct tm *tm = localtime(&now); char timestamp[64]; strftime(timestamp, sizeof(timestamp), “%Y-%m-%d %H:%M:%S”, tm); struct sockaddr_in addr; socklen_t addr_len = sizeof(addr); getpeername(client, (struct sockaddr*)&addr, &addr_len); char *ip = inet_ntoa(addr.sin_addr); printf("[%s] %s %s %s\n", timestamp, ip, method, url); } // / 处理目录列表 / // void serve_directory(int client, const char *path) { char buf[4096]; // 发送HTTP头 sprintf(buf, "HTTP/1.0 200 OK\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, SERVER_STRING); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-Type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); // 发送HTML头部 sprintf(buf, "<html><head><title>Index of %s</title></head>", path); send(client, buf, strlen(buf), 0); sprintf(buf, "<body><h1>Index of %s</h1><ul>", path); send(client, buf, strlen(buf), 0); // 打开目录 DIR *dir = opendir(path); if (dir) { struct dirent *ent; while ((ent = readdir(dir)) != NULL) { // 跳过隐藏文件 if (ent->d_name[0] == '.') continue; char full_path[1024]; sprintf(full_path, "%s/%s", path, ent->d_name); struct stat st; stat(full_path, &st); char size_buf[32]; if (S_ISDIR(st.st_mode)) { strcpy(size_buf, "[DIR]"); } else { if (st.st_size < 1024) { sprintf(size_buf, "%ld B", st.st_size); } else if (st.st_size < 1024 * 1024) { sprintf(size_buf, "%.1f KB", st.st_size / 1024.0); } else { sprintf(size_buf, "%.1f MB", st.st_size / (1024.0 * 1024)); } } sprintf(buf, "<li><a href=\"%s\">%s</a> - %s</li>", ent->d_name, ent->d_name, size_buf); send(client, buf, strlen(buf), 0); } closedir(dir); } // 发送HTML尾部 sprintf(buf, "</ul></body></html>\r\n"); send(client, buf, strlen(buf), 0); } // / 400 Bad Request / // void bad_request(int client) { char buf[1024]; sprintf(buf, "HTTP/1.0 400 BAD REQUEST\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<html><body><h1>400 Bad Request</h1></body></html>\r\n"); send(client, buf, strlen(buf), 0); } // / 403 Forbidden / // void forbidden(int client) { char buf[1024]; sprintf(buf, "HTTP/1.0 403 Forbidden\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, SERVER_STRING); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-Type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<html><body><h1>403 Forbidden</h1><p>Access to this resource is denied.</p></body></html>\r\n"); send(client, buf, strlen(buf), 0); } // / 发送文件内容 / // void cat(int client, FILE *resource) { char buf[65536]; fgets(buf, sizeof(buf), resource); while (!feof(resource)) { send(client, buf, strlen(buf), 0); fgets(buf, sizeof(buf), resource); } } // / 500 Internal Server Error / // void cannot_execute(int client) { char buf[1024]; sprintf(buf, "HTTP/1.0 500 Internal Server Error\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<html><body><h1>500 Internal Server Error</h1><p>CGI execution failed.</p></body></html>\r\n"); send(client, buf, strlen(buf), 0); } // / 错误处理 / // void error_die(const char *sc) { perror(sc); exit(1); } // / 执行CGI脚本 / // void execute_cgi(int client, const char *path, const char *method, const char *query_string) { char buf[65536]; 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)) numchars = get_line(client, buf, sizeof(buf)); else { numchars = get_line(client, buf, sizeof(buf)); while ((numchars > 0) && strcmp("\n", 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(client, buf, strlen(buf), 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) /* 子进程: CGI脚本 */ { 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 { /* 父进程 */ 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); } } // / 读取一行 / // 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(sock, &c, 1, 0); if (n > 0) { if (c == '\r') { n = recv(sock, &c, 1, MSG_PEEK); if ((n > 0) && (c == '\n')) recv(sock, &c, 1, 0); else c = '\n'; } buf[i] = c; i++; } else c = '\n'; } buf[i] = '\0'; return(i); } // / 发送HTTP头 / // void headers(int client, const char *filename, const char *content_type) { char buf[1024]; (void)filename; // 未使用 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); sprintf(buf, "Content-Type: %s\r\n", content_type); send(client, buf, strlen(buf), 0); strcpy(buf, "\r\n"); send(client, buf, strlen(buf), 0); } // / 404 Not Found / // void not_found(int client) { char buf[1024]; sprintf(buf, "HTTP/1.0 404 NOT FOUND\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, SERVER_STRING); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-Type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<html><body><h1>404 Not Found</h1><p>The requested URL was not found on this server.</p></body></html>\r\n"); send(client, buf, strlen(buf), 0); } // / 服务静态文件(修复二进制文件处理) / // void serve_file(int client, const char *filename) { FILE *resource = NULL; int numchars = 1; char buf[1024]; size_t bytes_read; long file_size; // 丢弃请求头 buf[0] = 'A'; buf[1] = '\0'; while ((numchars > 0) && strcmp("\n", buf)) numchars = get_line(client, buf, sizeof(buf)); // 使用二进制模式打开文件 resource = fopen(filename, "rb"); if (resource == NULL) { not_found(client); return; } // 获取文件大小 fseek(resource, 0, SEEK_END); file_size = ftell(resource); fseek(resource, 0, SEEK_SET); // 发送HTTP头 const char *content_type = get_mime_type(filename); // 创建并发送头部 char header_buf[2048]; sprintf(header_buf, "HTTP/1.0 200 OK\r\n"); send(client, header_buf, strlen(header_buf), 0); sprintf(header_buf, SERVER_STRING); send(client, header_buf, strlen(header_buf), 0); sprintf(header_buf, "Content-Type: %s\r\n", content_type); send(client, header_buf, strlen(header_buf), 0); sprintf(header_buf, "Content-Length: %ld\r\n", file_size); send(client, header_buf, strlen(header_buf), 0); sprintf(header_buf, "\r\n"); send(client, header_buf, strlen(header_buf), 0); // 使用二进制模式发送文件内容 while ((bytes_read = fread(buf, 1, sizeof(buf), resource)) > 0) { ssize_t sent = send(client, buf, bytes_read, MSG_NOSIGNAL); if (sent < 0) { // 处理发送错误(如连接关闭) break; } } fclose(resource); } // / 启动服务器 / // int startup(unsigned 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) { socklen_t namelen = sizeof(name); if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1) error_die("getsockname"); *port = ntohs(name.sin_port); } if (listen(httpd, 5) < 0) error_die("listen"); return(httpd); } // / 501 Not Implemented / // void unimplemented(int client) { char buf[1024]; sprintf(buf, "HTTP/1.0 501 Method Not Implemented\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, SERVER_STRING); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-Type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<html><body><h1>501 Not Implemented</h1><p>The requested method is not implemented.</p></body></html>\r\n"); send(client, buf, strlen(buf), 0); } // 设置套接字发送超时(在主循环accept后调用) void set_socket_timeout(int sockfd, int timeout_sec) { struct timeval tv; tv.tv_sec = timeout_sec; // 超时秒数 tv.tv_usec = 0; if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) { perror("setsockopt SO_SNDTIMEO failed"); } } // / 主函数 / // int main(void) { int server_sock = -1; unsigned short port = 8080; int client_sock = -1; struct sockaddr_in client_name; socklen_t client_name_len = sizeof(client_name); pthread_t newthread; server_sock = startup(&port); printf("HTTP server running on port %d\n", port); signal(SIGPIPE, SIG_IGN); while (1) { client_sock = accept(server_sock,(struct sockaddr *)&client_name,&client_name_len); if (client_sock == -1) { perror("accept"); continue; // 继续接受新连接,而不是退出 } // 动态分配内存传递socket描述符 int *pclient = malloc(sizeof(int)); if (!pclient) { perror("malloc failed"); close(client_sock); continue; } *pclient = client_sock; // 创建线程处理请求(不再在主线程设置超时) if (pthread_create(&newthread, NULL, accept_request, pclient) != 0) { perror("pthread_create"); free(pclient); close(client_sock); } else { // 分离线程,使其结束后自动释放资源 pthread_detach(newthread); } } close(server_sock); return(0); } 这是我的多线程版本webserver。我现在需要写一版以IO多路复用方式实现的webserver
08-09
import http.server import time import socketserver import os import threading import socket import json #下面的导入从SimpleHTTPServer.py复制: import posixpath import urllib.parse import cgi import sys import shutil import mimetypes import io import re #从jfrog下载并解压文件-开始 from file_downloader import FileDownloader # 创建 FileDownloader 实例 print(f"start download file \n") downloader = FileDownloader() # 执行下载和解压 downloader.execute() #从jfrog下载并解压文件-结束 PORT = 8000 i = 1 class MyThreadingHTTPServer(socketserver.ThreadingTCPServer): allow_reuse_address = 1 def server_bind(self): """Override server_bind to store the server name.""" socketserver.TCPServer.server_bind(self) host, port = self.socket.getsockname()[:2] self.server_name = socket.getfqdn(host) self.server_port = port #Handler = SimpleHTTPServer.SimpleHTTPRequestHandler class MyHTTPRequestHandler(http.server.SimpleHTTPRequestHandler): def dumpRequestHeaders(self): pass #print 'dumpRequestHeaders: raw_requestline=%s \nheaders=\n%s' % (self.raw_requestline,self.headers) def copyfile_by_range(self, fin, fout, start, end): print( "copyfile_by_range: start=%d end=%d" % (start, end)) READ_BUFFER_SIZE = 4*1024; fin.seek(start, os.SEEK_SET) if end<0: #代表原始Range请求未指定完整范围,只指定了开始位置 buf = fin.read(READ_BUFFER_SIZE) #FIXME:健壮性fix,如果读到内容小于size参数?需要判断len(buf) if len(buf)!=READ_BUFFER_SIZE: pass #print "copyfile_by_range: len(buf)!=READ_BUFFER_SIZE 1 len(buf)=%d" % (len(buf)) while buf: fout.write(buf) fout.flush() buf = fin.read(READ_BUFFER_SIZE) if len(buf)==0: break #?? if len(buf)!=READ_BUFFER_SIZE: pass #print "copyfile_by_range: len(buf)!=READ_BUFFER_SIZE 2 len(buf)=%d" % (len(buf)) fout.write(buf) break else: bytes_left = end-start+1 while bytes_left >= READ_BUFFER_SIZE: buf = fin.read(READ_BUFFER_SIZE) if len(buf)!=READ_BUFFER_SIZE: pass #print "copyfile_by_range: len(buf)!=READ_BUFFER_SIZE 3 len(buf)=%d" % (len(buf)) fout.write(buf) bytes_left = bytes_left - READ_BUFFER_SIZE if bytes_left>0: buf = fin.read(bytes_left) if len(buf)!=bytes_left: pass #print "copyfile_by_range: len(buf)!=bytes_left len(buf)=%d bytes_left=" % (len(buf), bytes_left) fout.write(buf) def do_GET(self): self.dumpRequestHeaders() #用于查看客户端浏览器的User-Agent设置; # #SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self) f, range = self.send_head() #原来的send_head这个函数实现有点莫名其妙? if f: if range: #注意,响应头部已经在send_head()里设置完成了,这里只需要调整io读写指针 self.copyfile_by_range(f, self.wfile, range[0], range[1]) else: self.copyfile(f, self.wfile) f.close() #重载SimpleHTTPServer.py里的实现,以实现:(1)按修改日期排序(2)正确显示中文 #TODO:支持更多查询参数?html输出代码美化? def list_directory(self, path): """Helper to produce a directory listing (absent index.html). Return value is either a file object, or None (indicating an error). In either case, the headers are sent, making the interface the same as for send_head(). """ try: list = os.listdir(path) except os.error: self.send_error(404, "No permission to list directory") return None #list.sort(key=lambda a: a.lower()) ''' def compare_by_modtime(x, y): stat_x = os.stat(path + "/" + x) stat_y = os.stat(path + "/" + y) if stat_x.st_mtime < stat_y.st_mtime: return -1 elif stat_x.st_mtime > stat_y.st_mtime: return 1 else: return 0 list.sort(lambda x,y: compare_by_modtime(y,x)) #最近修改的排在前面 ''' def key_by_mtime(a): try: return - os.stat(path + "/" + a).st_mtime except Exception as e: return 0 #?? list.sort(key=key_by_mtime) f = [] enc = "gb18030"; displaypath = cgi.escape(urllib.parse.unquote(self.path)) f.append('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" ' '"http://www.w3.org/TR/html4/strict.dtd">') f.append('<html>\n<head>') f.append('<meta http-equiv="Content-Type" ' 'content="text/html; charset=%s">' % enc) f.append("<title>Directory listing for %s</title>\n" % displaypath) f.append("<body>\n<h2>Directory listing for %s</h2>\n" % displaypath) f.append("<hr>\n<ul>\n") for name in list: fullname = os.path.join(path, name) displayname = linkname = name is_dir = False # Append / for directories or @ for symbolic links if os.path.isdir(fullname): displayname = name + "/" linkname = name + "/" is_dir = True if os.path.islink(fullname): displayname = name + "@" # Note: a link to a directory displays with @ and links with / f.append('<li><a href="%s">%s</a>\n' % (urllib.parse.quote(linkname), cgi.escape(displayname))) #self.path是浏览器请求路径,而path是本地文件系统路径 if not is_dir and name.endswith('.mp4'): f.append('|<a href="/playvideo?path=%s">播放</a>\n' % (urllib.parse.quote( os.path.join(self.path, name)))) f.append('</li>') f.append("</ul>\n<hr>\n</body>\n</html>\n") encoded = '\n'.join(f).encode(enc, 'surrogateescape') b = io.BytesIO() b.write(encoded) b.seek(0) self.send_response(200,"success") encoding = "gbk" #sys.getfilesystemencoding() self.send_header("Content-Type", "text/html; charset=%s" % encoding) self.send_header("Content-Length", str(len(encoded))) self.end_headers() return b #TODO:支持Range请求,这样可以提供基于HTTP的视频流媒体服务 def send_head(self): """ overwrite send_head to set Last-Modified & Expires to disable browser cache; """ enc = "gb18030"; unquoted_path = urllib.parse.unquote(self.path) print( "send_head: self.path=%s unquoted_path=%s" % (self.path, unquoted_path)) PLAYVIDEO_REQUEST = re.compile(r'/playvideo\?path=(.+)$') m = PLAYVIDEO_REQUEST.match(unquoted_path) if m: #TODO: 重构这里的代码 video_path = m.group(1) print( "send_head: video_path=%s" % video_path) self.send_response(200) self.send_header("Content-Type", "text/html") self.end_headers() self.wfile.write(('<video src="%s" controls></video>' % video_path).encode(enc, 'surrogateescape')) #注意,这个地方不需要urllib.quote return (None,None) path = self.translate_path(self.path) f = None if os.path.isdir(path): if not self.path.endswith('/'): # redirect browser - doing basically what apache does self.send_response(301) sa = s.socket.getsockname() self.send_header("Location", "http://" + str(sa[0]) + ":" + str(sa[1]) + self.path + "/icc_delta.zip_E.51") self.wfile.flush() time.sleep(1) self.end_headers() return (None,None) for index in "index.html", "index.htm": index = os.path.join(path, index) if os.path.exists(index): path = index break else: return (self.list_directory(path), None) ctype = self.guess_type(path) try: # Always read in binary mode. Opening files in text mode may cause # newline translations, making the actual size of the content # transmitted *less* than the content-length! f = open(path, 'rb') #Get file size: f.seek(0, os.SEEK_END) filesize = f.tell() f.seek(0, os.SEEK_SET) #TODO: 检查原始请求是否指定了Range头部 if self.headers.get("Range"): range_value = self.headers["Range"] #range_value = "bytes=31219987-71219986" print( "send_head: range_value=[%s]" % range_value) #直接使用正则表达式匹配: Range: bytes=100- HTTP_RANGE_HEADER = re.compile(r'bytes=([0-9]+)\s*\-\s*(([0-9]+)?)') m = re.match(HTTP_RANGE_HEADER, range_value) if m: start_str = m.group(1) start = int(start_str) end_str = m.group(2) end = -1 if len(end_str)>0: end = int(end_str) #现在可以写Range响应头部了: self.send_response(206, "Partial Content") self.send_header("Content-Type", ctype) if end==-1: self.send_header("Content-Length", str(filesize-start)) else: self.send_header("Content-Length", str(end-start+1)) self.send_header("Accept-Ranges", "bytes") if end<0: content_range_header_value = "bytes %d-%d/%d" % (start, filesize-1, filesize) else: content_range_header_value = "bytes %d-%d/%d" % (start, end, filesize) self.send_header("Content-Range", content_range_header_value) print( "send_head: ok, serve 206 for Range request %s-%s,Content-Range=%s" % (start_str, end_str, content_range_header_value)) self.send_header("Connection", "close") self.end_headers() return (f, [start, end]) else: print( "send_head: error! INVALID Range request header!!") self.send_error(400, "Bad Request") self.wfile.flush() self.end_headers() return (None,None) except IOError: self.send_error(404, "File not found") return (None,None) self.send_response(200,"success") self.send_header("Content-Type", ctype) file_stat = os.fstat(f.fileno()) self.send_header("Content-Length", str(file_stat[6])) #self.send_header("Last-Modified", self.date_time_string(file_stat.st_mtime)) self.send_header("Last-Modified", self.date_time_string(time.time())) #self.send_header("Expires", self.date_time_string(time.time()+5)) #self.send_header("Cache-control", "no-cache, no-store, must-revalidate, max-age=0, proxy-revalidate, no-transform") #self.send_header("Pragma", "no-cache") self.send_header("Connection", "close") self.end_headers() return (f, None) def do_POST(self): print(self.path) if self.path == '/upload/log_package': self.handle_chunked_upload() if self.path == '/request_package_url.cgi': self.handle_post_url() else: # 处理其他POST请求(如果有的话) pass def handle_post_url(self): # 构造符合指定格式的响应 response = [ {"ModuleID":"51","URL":"http://0.0.0.0:8000/ICC_mpu/cloud_iccmpu322483.51"} ] print(response) # 返回处理结果 self.send_response(200, "success") self.send_header('Content-Type', 'application/json') self.end_headers() self.wfile.write(json.dumps(response).encode('utf-8')) return def handle_chunked_upload(self): global i print(i) content_type, _ = cgi.parse_header(self.headers.get('Content-Type')) content_range, _ = cgi.parse_header(self.headers.get('Content-Range')) lst = re.split(r"[-/]", content_range) print(lst) if content_type == 'multipart/form-data': form_data = cgi.FieldStorage( fp=self.rfile, headers=self.headers, environ={'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': self.headers['Content-Type']} ) # 获取分片相关信息 total_chunks = int(lst[2]) current_chunk = int(lst[1]) file_name = "1632778555351_0123456789V000001_20210927_213557_14869_ota_V1.zip" # 将分片保存到指定位置 upload_path = '/fota/' # 替换为你希望保存上传文件的目录 part = file_name + '_part_' + str(i) file_path = os.path.join(upload_path, part) print(file_path) with open(file_path, 'wb') as new_file: new_file.write(form_data['file'].file.read()) new_file.flush() os.fsync(new_file.fileno()) # 如果是最后一个分片,组合分片 if current_chunk == total_chunks: combined_file_path = os.path.join(upload_path, file_name) with open(combined_file_path, 'ab') as combined_file: for chunk_num in range(1, i+1): print(chunk_num) part_num = file_name + '_part_' + str(chunk_num) part_file_path = os.path.join(upload_path, part_num) print(part_file_path) with open(part_file_path, 'rb') as part_file: combined_file.write(part_file.read()) print("write success") os.remove(part_file_path) # 删除已经组合的分片文件 print("delete success") # 构造符合指定格式的响应 response = { 'code': 200, 'message': 'success', 'data': { 'fileUrl': 'http://0.0.0.0:8000/upload/log_package' # 替换为服务器地址 } } # 用JSON格式回应客户端 self.send_response(200, "success") self.send_header('Content-Type', 'application/json') self.end_headers() self.wfile.write(json.dumps(response).encode('utf-8')) return # 如果不是最后一个分片,简单返回成功 response = { 'code': 200, 'message': 'success' } # 用JSON格式回应客户端 i = i + 1 self.send_response(200, "success") self.send_header('Content-Type', 'application/json') self.end_headers() self.wfile.write(json.dumps(response).encode('utf-8')) return else: i = 1 self.send_error(400, '请求错误:文件上传的内容类型无效') return s = MyThreadingHTTPServer(("", PORT), MyHTTPRequestHandler) sa = s.socket.getsockname() print("Serving MyThreadingHTTPServer on", sa[0], "port", sa[1], "...") s.serve_forever() 参考这个python 代码的实现来写c++的
最新发布
10-24
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值