For some reasons and educational purposes, I developed myself a C++ HTTP server from scratch which should capture file uploads from an HTML page with AJAX handling dynamic requests and save it in the same directory where the server program is located.
The server works fine for the .txt files. The files get uploaded and saved in server directory without any flaws. Everything remains perfect but the problem begins when I try to upload binary files like , executable files , video files , music files and others. Basically, the server is working fine with .txt files not with others. Whenever a binary file is uploaded, the output file at the server side gets somehow corrupted.
I know that there are PHP and NodeJS frameworks for those operations but actually I will be deploying this project on a bare metal controller server which has a very limited amount of resources that cannot withstand complex frameworks. So C++ server using scratch seems to be the only way of accomplishing my goal. Although I cannot directly port this code into the controller, the procedure remains the same. To learn about the procedure first hand, I have to go through some experiments on Windows using Windows Sockets.
Here is what I have tried so far,
The C++ HTTP server code.
#include <stdio.h>
#include <math.h>
#include <cmath>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <winsock2.h>
#include <windows.h>
#define PORT 12345
WSADATA ws;
char Command[8888];
char FILEDATA[(2 * 1024)]; // FILE HEADER and BUFFER
SOCKET soc, acpt;
FILE *f ;
unsigned long long FileSize(char * direc){
unsigned long long size = 0;
FILE *fsize = fopen(direc,"rb");
fseek(fsize,0,SEEK_END);
size = _ftelli64(fsize);
fseek(fsize,0,SEEK_SET);
fclose(fsize);
return size;
}
void Serve(){
int optval = 1;
int RecvBytes=0;
sockaddr_in addr, peerAddr;
struct timeval RequestTimeout = {0,5000000} , DownloadTimeout= {0,500000};
char URL[4000]={0};
char FileName[260]={0};
addr.sin_addr.s_addr = inet_addr("0.0.0.0");
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
fd_set rfd;
FD_ZERO(&rfd);
soc = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
setsockopt(soc,SOL_SOCKET,SO_REUSEADDR,(const char*)&optval,sizeof(optval));
bind(soc,(sockaddr*)&addr,sizeof(addr));
listen(soc,100);
acpt = accept(soc,NULL,NULL);
FD_SET(acpt,&rfd);
int connstat = select(acpt+1,&rfd,NULL,NULL,&RequestTimeout); // act only if a stable reading can be performed
if(connstat){
int x = 0;
RecvBytes = recv(acpt,Command,sizeof(Command),0); // Receives HTTP header
RecvBytes = recv(acpt,FILEDATA,sizeof(FILEDATA),0); // Receives File headers and content
char * namePtr = strstr(FILEDATA,"\r\n\r\n");
namePtr = strstr(FILEDATA,"filename=\"") + strlen("filename "); // Extraction of File Name begins here
while(namePtr[x]!='"'){
x++;
}
strncpy(FileName,namePtr,x); // Extraction of file name ends here.
printf("NAME: %s\n\n",FileName);
int NumberOfBytes = (&strstr(FILEDATA,"\r\n\r\n")[0]+2 - &FILEDATA[0])+2;
f = fopen(FileName,"wb"); // Open file for writing
fwrite(strstr(FILEDATA,"\r\n\r\n")+3,1,RecvBytes - NumberOfBytes ,f); // Received Number of Bytes - Number of Bytes Till Header
while(select(acpt+1,&rfd,NULL,NULL,&DownloadTimeout)){ // Keep reading if more data remains available
RecvBytes = recv(acpt,FILEDATA,sizeof(FILEDATA),0); // receive
if(RecvBytes==0){
break;
}
fwrite(FILEDATA,1,RecvBytes,f);
}
}
fclose(f);
unsigned long long truncFile = FileSize(FileName) - 45;
f = fopen(FileName,"r+"); // truncating file to remove the webkit boundary substring at the end of the file.
ftruncate(fileno(f),truncFile);
fclose(f);
printf("OUTPUT FILE SIZE: %llu\n\n",FileSize(FileName));
MessageBeep(MB_ICONERROR); // A beep means operation is complete
shutdown(acpt,2);
closesocket(acpt);
closesocket(soc);
shutdown(soc,2);
acpt = NULL;
soc = NULL;
memset(FILEDATA,'\0',sizeof(FILEDATA));
}
int main(){
//printf("%d\n",strlen("------WebKitFormBoundary5BlPHcjSKYuZFzV3--")); 42
WSAStartup(MAKEWORD(2,2),&ws);
while(1){
Serve();
}
return 0;
}
Now the AJAX HTML page used for uploading ,
<!DOCTYPE html>
<html>
<body>
The content of the body element is displayed in your browser.
<input type="file" id="image-file">
<button onclick="Send()">Send</button>
<script>
function Send(){
let photo = document.getElementById("image-file").files[0]; // file from input
let req = new XMLHttpRequest();
let formData = new FormData();
formData.append("photo", photo);
req.open("POST", 'http://localhost:12345/');
req.send(formData);
}
</script>
</body>
</html>
I would like to have a quickest solution to this problem. Thank you for understanding my concern, Regards...
Finally after a lot of struggle, these codes do work perfectly!
C HTTP Server Downloader
#include <stdio.h>
#include <math.h>
#include <cmath>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <winsock2.h>
#include <windows.h>
#define PORT 12345
WSADATA ws;
char Command[8888];
char FILEDATA[(16 * 1024 * 1024)]; // FILE HEADER and BUFFER
SOCKET soc, acpt;
FILE *f ;
unsigned long long FileSize(char * direc){
unsigned long long size = 0;
FILE *fsize = fopen(direc,"rb");
fseek(fsize,0,SEEK_END);
size = _ftelli64(fsize);
fseek(fsize,0,SEEK_SET);
fclose(fsize);
return size;
}
void Serve(){
int optval = 1;
int RecvBytes=0;
sockaddr_in addr, peerAddr;
struct timeval RequestTimeout = {0,5000000} , DownloadTimeout= {0,500000};
char URL[4000]={0};
char FileName[260]={0};
addr.sin_addr.s_addr = inet_addr("0.0.0.0");
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
fd_set rfd;
FD_ZERO(&rfd);
soc = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
setsockopt(soc,SOL_SOCKET,SO_REUSEADDR,(const char*)&optval,sizeof(optval));
bind(soc,(sockaddr*)&addr,sizeof(addr));
listen(soc,100);
acpt = accept(soc,NULL,NULL);
FD_SET(acpt,&rfd);
int connstat = select(acpt+1,&rfd,NULL,NULL,&RequestTimeout);
if(connstat){
int x = 0;
RecvBytes = recv(acpt,Command,sizeof(Command),0); // Receives HTTP header
RecvBytes = recv(acpt,FILEDATA,sizeof(FILEDATA),0); // Receives File headers and content
char * namePtr = strstr(FILEDATA,"\r\n\r\n");
namePtr = strstr(FILEDATA,"filename=\"") + strlen("filename "); // Extraction of File Name begins here
while(namePtr[x]!='"'){
x++;
}
strncpy(FileName,namePtr,x); // Extraction of file name ends here.
printf("NAME: %s\n\n",FileName);
int NumberOfBytes = (&strstr(FILEDATA,"\r\n\r\n")[0]+2 - &FILEDATA[0])+2;
f = fopen(FileName,"wb"); // Open file for writing
printf("CONTENTS: %d ", &strstr(FILEDATA,"\r\n\r\n")[0]+2 - &FILEDATA[0]);
fwrite(strstr(FILEDATA,"\r\n\r\n")+4,1,RecvBytes - NumberOfBytes ,f); // Received Number of Bytes - Number of Bytes Till Header
while(select(acpt+1,&rfd,NULL,NULL,&DownloadTimeout)){ // Keep reading if more data remains available
RecvBytes = recv(acpt,FILEDATA,sizeof(FILEDATA),0); // receive
if(RecvBytes==0){
break;
}
fwrite(FILEDATA,1,RecvBytes,f);
}
}
fclose(f);
unsigned long long truncFile = FileSize(FileName) - 45;
f = fopen(FileName,"r+b"); // truncating file to remove the webkit boundary
ftruncate(fileno(f),truncFile);
fclose(f);
printf("OUTPUT FILE SIZE: %llu\n\n",FileSize(FileName));
MessageBeep(MB_ICONERROR); // A beep means operation is complete
shutdown(acpt,2);
closesocket(acpt);
closesocket(soc);
shutdown(soc,2);
acpt = NULL;
soc = NULL;
memset(FILEDATA,'\0',sizeof(FILEDATA));
}
int main(){
//printf("%d\n",strlen("------WebKitFormBoundary5BlPHcjSKYuZFzV3--")); 42
WSAStartup(MAKEWORD(2,2),&ws);
while(1){
Serve();
}
return 0;
}
HTML with AJAX Uploader
<!DOCTYPE html>
<html>
<body>
The content of the body element is displayed in your browser.
<input type="file" id="image-file">
<button onclick="Send()">Send</button>
<script>
function Send(){
let photo = document.getElementById("image-file").files[0]; // file from input
let req = new XMLHttpRequest();
let formData = new FormData();
formData.append("photo", photo);
req.open("POST", 'http://localhost:12345/');
req.send(formData);
}
</script>
</body>
</html>
The C HTTP Server code above processes and downloads the upload request perfectly in the directory where the server is saved without corrupting the file.