実現したいこと
HTTPサーバーが正しくファイルアップロードを処理し、画像ファイルをサーバーに保存するようにする。
発生している問題・分からないこと
HTTPサーバーをC言語で実装しています。POSTリクエストを処理し、クライアントから送信されたファイルをサーバーに保存したいのですが、リクエストボディの解析が正しく行われず、ファイルデータが正しく保存されません。現在のコードでは、POSTリクエストのヘッダーは正しく受信されていますが、ファイルデータの部分がうまく処理されていない状態です。
何か解決法をご教示いただけますと幸いです。
該当のソースコード
C
1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4#include <unistd.h> 5#include <sys/socket.h> 6#include <netinet/in.h> 7#include <arpa/inet.h> 8#include <fcntl.h> 9#include <errno.h> 10#include <sys/stat.h> 11#include <sys/types.h> 12#include <sys/wait.h> 13 14#define PORT 8080 15#define BUFSIZE 8192 16 17void error(const char *msg) { 18 perror(msg); 19 exit(1); 20} 21 22void handle_request(int newsockfd) { 23 char buffer[BUFSIZE]; 24 int n; 25 char boundary[256]; 26 27 // Read the initial request into the buffer 28 n = read(newsockfd, buffer, BUFSIZE - 1); 29 if (n < 0) error("ERROR reading from socket"); 30 buffer[n] = 0; 31 32 printf("Here is the message: %s\n", buffer); 33 34 if (strncmp(buffer, "GET ", 4) == 0) { 35 // Handle GET request 36 char *filepath = strtok(buffer + 4, " "); 37 if (strcmp(filepath, "/") == 0) { 38 filepath = "/index.html"; 39 } 40 41 char fullpath[BUFSIZE] = "."; 42 strncat(fullpath, filepath, BUFSIZE - strlen(fullpath) - 1); 43 44 int filefd = open(fullpath, O_RDONLY); 45 if (filefd < 0) { 46 char *not_found = "HTTP/1.1 404 Not Found\r\nContent-Length: 13\r\n\r\n404 Not Found"; 47 write(newsockfd, not_found, strlen(not_found)); 48 } else { 49 char response[BUFSIZE]; 50 sprintf(response, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"); 51 write(newsockfd, response, strlen(response)); 52 53 while ((n = read(filefd, buffer, BUFSIZE)) > 0) { 54 write(newsockfd, buffer, n); 55 } 56 close(filefd); 57 } 58 } else if (strncmp(buffer, "POST ", 5) == 0) { 59 // Handle POST request 60 char *content_length_str = strstr(buffer, "Content-Length: "); 61 int content_length = 0; 62 if (content_length_str) { 63 sscanf(content_length_str, "Content-Length: %d", &content_length); 64 } 65 66 char *content_type_str = strstr(buffer, "Content-Type: multipart/form-data; boundary="); 67 if (content_type_str) { 68 sscanf(content_type_str, "Content-Type: multipart/form-data; boundary=%s", boundary); 69 } 70 71 // Read the remaining data of the request 72 if (n < content_length) { 73 int remaining = content_length - n; 74 char *full_request = malloc(content_length + 1); 75 memcpy(full_request, buffer, n); 76 int read_bytes = read(newsockfd, full_request + n, remaining); 77 if (read_bytes < 0) { 78 error("ERROR reading remaining bytes from socket"); 79 } 80 full_request[content_length] = 0; 81 strcpy(buffer, full_request); 82 free(full_request); 83 } 84 85 char *part_start = strstr(buffer, boundary); 86 if (part_start) { 87 part_start += strlen(boundary) + 2; // Skip boundary and \r\n 88 89 while (part_start && *part_start != '-' && *part_start != 0) { 90 char *header_end = strstr(part_start, "\r\n\r\n"); 91 if (header_end) { 92 header_end += 4; // Skip past \r\n\r\n 93 94 char *part_end = strstr(header_end, boundary); 95 if (part_end) { 96 part_end -= 2; // Exclude trailing \r\n 97 98 // Check for Content-Disposition header with filename 99 char *content_disposition = strstr(part_start, "Content-Disposition: "); 100 if (content_disposition && strstr(content_disposition, "filename=")) { 101 size_t data_length = part_end - header_end; 102 if (data_length > 0) { 103 FILE *file = fopen("upload.jpg", "wb"); 104 if (file) { 105 fwrite(header_end, 1, data_length, file); 106 fclose(file); 107 108 char *ok_response = "HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nOK"; 109 write(newsockfd, ok_response, strlen(ok_response)); 110 } else { 111 char *internal_error = "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 21\r\n\r\n500 Internal Server Error"; 112 write(newsockfd, internal_error, strlen(internal_error)); 113 } 114 } else { 115 char *bad_request = "HTTP/1.1 400 Bad Request\r\nContent-Length: 27\r\n\r\n400 Bad Request: No file data"; 116 write(newsockfd, bad_request, strlen(bad_request)); 117 } 118 } 119 120 part_start = part_end + strlen(boundary) + 2; // Skip past boundary and \r\n 121 } else { 122 break; 123 } 124 } else { 125 break; 126 } 127 } 128 } else { 129 char *bad_request = "HTTP/1.1 400 Bad Request\r\nContent-Length: 15\r\n\r\n400 Bad Request"; 130 write(newsockfd, bad_request, strlen(bad_request)); 131 } 132 } else { 133 char *bad_request = "HTTP/1.1 400 Bad Request\r\nContent-Length: 15\r\n\r\n400 Bad Request"; 134 write(newsockfd, bad_request, strlen(bad_request)); 135 } 136} 137 138int main() { 139 int server_fd, new_socket; 140 struct sockaddr_in address; 141 int addrlen = sizeof(address); 142 143 if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { 144 perror("Socket creation failed"); 145 exit(EXIT_FAILURE); 146 } 147 148 address.sin_family = AF_INET; 149 address.sin_addr.s_addr = INADDR_ANY; 150 address.sin_port = htons(PORT); 151 152 if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { 153 perror("Bind failed"); 154 close(server_fd); 155 exit(EXIT_FAILURE); 156 } 157 158 if (listen(server_fd, 10) < 0) { 159 perror("Listen failed"); 160 close(server_fd); 161 exit(EXIT_FAILURE); 162 } 163 164 printf("Server is listening on port 8080\n"); 165 166 while (1) { 167 printf("Waiting for connections...\n"); 168 169 if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) { 170 perror("Accept failed"); 171 continue; 172 } 173 174 pid_t pid = fork(); 175 if (pid < 0) { 176 perror("Fork failed"); 177 close(new_socket); 178 continue; 179 } 180 181 if (pid == 0) { 182 close(server_fd); 183 handle_request(new_socket); 184 exit(0); 185 } else { 186 close(new_socket); 187 waitpid(-1, NULL, WNOHANG); // Prevent zombie processes 188 } 189 } 190 191 close(server_fd); 192 return 0; 193} 194
試したこと・調べたこと
- teratailやGoogle等で検索した
- ソースコードを自分なりに変更した
- 知人に聞いた
- その他
上記の詳細・結果
POSTリクエストの読み込み処理とMultipart/form-dataのバウンダリの解析処理を追加しましたが、ファイルデータの部分がうまく処理されません。
補足
FW/ツールのバージョンや、補足事項があれば入力してください
開発環境:MacOS 10.15.7
コンパイラ:gcc version 10.2.0
クライアント:Google Chrome 126.0.0.0
