Search code examples
c++windowshttpxmlhttprequestwinsock2

C++ HTTP server file downloader problem with Ajax uploader


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...


Solution

  • 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.