Freelancer
Freelancer

Reputation: 844

Sending html file with HTTP protocol over tcp and browser shows error

I'm am writing a HTTP web server when I send a text file with the equivalent content of the HTML file to the browser the browser shows it correctly but when I send the HTML file itself browser shows the HTML page for a second and then the "the connection was reset" error shows up.

I have noticed that the text file is bigger than the HTML file but I have no Idea why

text size = 286 byte

HTML size = 142 byte

and this is the HTML code:

<!DOCTYPE html>
<html>
<body>

<p>This is a paragraph.</p>
<p>This is a paragraph.</p>
<p>This is a paragraph.</p>

</body>
</html>

this is my code:

char sendBuffer[500];

FILE *sendFile = fopen("foo.html", "r");
fseek(sendFile, 0L, SEEK_END);
int sz = ftell(sendFile);
fseek(sendFile, 0L, SEEK_SET);

string s1;
s1="HTTP/1.1 200 OK\nContent-length: " + to_string(sz) + "\n";
std::vector<char> writable(s1.begin(), s1.end());
writable.push_back('\0');

strcpy(sendBuffer,(const char *)&writable[0]);
int c=send(connected,(const char*)&sendBuffer,strlen(&writable[0]),0);
printf("\nSent : %s\n",sendBuffer);
strcpy(sendBuffer,"Content-Type: text/html\n\n");
c=send(connected,(const char*)&sendBuffer,strlen("Content-Type: text/html\n\n"),0);
printf("\nSent : %s\n",sendBuffer);

char send_buffer[300];

while( !feof(sendFile) )
{
    int numread = fread(send_buffer, sizeof(unsigned char), 300, sendFile);
    if( numread < 1 ) break; // EOF or error

    char *send_buffer_ptr = send_buffer;
    do {
        int numsent = send(connected, send_buffer_ptr, numread, 0);
        if( numsent < 1 ) // 0 if disconnected, otherwise error
         {
            if( numsent < 0 ) {
                if( WSAGetLastError() == WSAEWOULDBLOCK )
                {
                    fd_set wfd;
                    FD_ZERO(&wfd);
                    FD_SET(connected, &wfd);

                    timeval tm;
                    tm.tv_sec = 10;
                    tm.tv_usec = 0;

                    if( select(0, NULL, &wfd, NULL, &tm) > 0 )
                        continue;
               }
           }

        break; // timeout or error
    }

    send_buffer_ptr += numsent;
    numread -= numsent;
}
while( numread > 0 );
}

Here is the other part of code that is used just before the code above:

int sock, connected, bytes_recieved , _true = 1 , portNumber;
char send_data [1024] , recv_data[1024];      
struct sockaddr_in server_addr,client_addr;   
int sin_size;

time_t t = time(NULL);
struct tm tm = *localtime(&t);
char date[50];

if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
    perror("Unable to create the Socket");
    exit(1);
}

if (setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(const char*)&_true,sizeof(int)) == -1) {
    perror("Unable to Setsockopt");
    exit(1);
}
char *server_address="127.1.1.1";
portNumber=8080;
server_addr.sin_family = AF_INET; 
server_addr.sin_port = htons(portNumber);
server_addr.sin_addr.s_addr = inet_addr("127.1.1.1");//inet_pton(AF_INET,"127.0.0.1",&server_addr.sin_addr);//INADDR_ANY;

string host=server_address+':'+to_string(portNumber);


memset(&(server_addr.sin_zero),0,8);//sockaddr_in zero padding is needed
if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr))==-1) //bind the socket to a local address
{
    perror("Unable to bind");
    exit(1);
}

if (listen(sock, 5) == -1) //listen to the socket with the specified waiting queue size
{
    perror(" Listen");
    exit(1);
}

cout << "MyHTTPServer waiting on port 8080" << endl;
fflush(stdout);

sin_size = sizeof(struct sockaddr_in);
connected = accept(sock, (struct sockaddr *)&client_addr,&sin_size);

cout<< "I got a connection from (" << inet_ntoa(client_addr.sin_addr) << "," << ntohs(client_addr.sin_port) << ')' << endl;

Upvotes: 4

Views: 3801

Answers (2)

Morpfh
Morpfh

Reputation: 4093

A half way answer. First off, even if “Using \n works” it is breaching the standard. One should use CRLF. Use CRLF. Period.

For the rest of the code. I doubt this is going to change many things, but I would have re-structured the code a bit. It is to much going on in the send function.

Have separated out the sending of data to own function. You could also consider even separating out send header to it's own function – if you find a good way to structure it. When you expand to send text or html or etc. and so on and so forth, you definitively should separate out header to own function. Doing it at an early stage would be helpful.

Only meant as a crude start.

int send_data(int soc, const char *buf, size_t len)
{
    ssize_t sent;

    do {

        /* Use iharob code or similar here */
        /* Return something <> 0 on error. */

        sent = send(soc, buf, len, 0);

        buf += sent;
        len -= sent;
    } while (len > 0);

    return 0;
}

int send_file(int soc, const char *fn)
{
    char buf[500];
    FILE *fh;
    long sz;
    size_t len;
    int err = 0;

    if (!(fh = fopen(fn, "r"))) {
        perror("fopen");
        return 1;
    }
    fseek(fh, 0L, SEEK_END);
    sz = ftell(fh);
    fseek(fh, 0L, SEEK_SET);

    /* Consider adding Date + Server here. */
    len = sprintf(buf,
            "HTTP/1.1 200 OK\r\n"
            "Content-length: %ld\r\n"
            "Content-Type: text/html\r\n"
            "Server: FooBar/0.0.1\r\n"
            "\r\n", sz
    );
    if (len < 0) {
        err = 3;
        fprintf(stderr, "Error writing header.\n");
        goto fine;
    }

    /* Debug print. */
    fprintf(stderr, "Header[%d]:\n'%s'\n", len, buf);

    if ((err = send_data(soc, buf, len)) != 0) {
        fprintf(stderr, "Error sending header.\n");
        goto fine;
    }

    while (!feof(fh)) {
        len = fread(buf, sizeof(char), 500, fh);
        if (len < 1)
            break;
        if ((err = send_data(soc, buf, len))) {
            fprintf(stderr, "Error sending file.\n");
            goto fine;
        }
    }
    if ((err = ferror(fh))) {
        fprintf(stderr, "Error reading file.\n");
        perror("fread");
    }
fine:
    fclose(fh);
    return err;
}

Upvotes: 1

Iharob Al Asimi
Iharob Al Asimi

Reputation: 53016

You have two important problems I can see

  1. Your are passing send paremeters wrong, this line (very important)

    int c=send(connected,(const char*)&sendBuffer,strlen(&writable[0]),0);
    

    should be

    int c=send(connected,(const char*) sendBuffer,strlen(&writable[0]),0);
    /*                                ^ 
     *                                No ampersand
     */
    

    since the sendBuffer array decays to a pointer and you don't need that.

  2. You are passing the first parameter of select wrong too from the manual

    nfds is the highest-numbered file descriptor in any of the three sets, plus 1

    so in your case it should be

    if (select(connected + 1, NULL, &wfd, NULL, &tm) > 0)
    

    and you are using it after you call send you must call it before to see if it is possible to write to the file descriptor.

Your code is a little bit too complicated for the task it's designed to so I propose the following solution with the mentions problems fixed and some other ones improved

string       text;
stringstream stream;

FILE *sendFile = fopen("foo.html", "r");
if (sendFile == NULL) /* check it the file was opened */
    return;

fseek(sendFile, 0L, SEEK_END);
/* you can use a stringstream, it's cleaner */
stream << "HTTP/1.1 200 OK\nContent-length: " << ftell(sendFile) << "\n";
fseek(sendFile, 0L, SEEK_SET);

text = stream.str();
/* you don't need a vector and strcpy to a char array, just call the .c_str() member
 * of the string class and the .length() member for it's length
 */
send(connected, text.c_str(), text.length(), 0);

std::cout << "Sent : " <<  text << std::endl;

text = "Content-Type: text/html\n\n";
send(connected, text.c_str(), text.length(), 0);

std::cout << "Sent : %s" << text << std::endl;
while (feof(sendFile) == 0)
{
    int  numread;
    char sendBuffer[500];

    numread = fread(sendBuffer, sizeof(unsigned char), 300, sendFile);
    if (numread > 0)
    {
        char *sendBuffer_ptr;

        sendBuffer_ptr = sendBuffer;
        do {
            fd_set  wfd;
            timeval tm;

            FD_ZERO(&wfd);
            FD_SET(connected, &wfd);

            tm.tv_sec  = 10;
            tm.tv_usec = 0;
            /* first call select, and if the descriptor is writeable, call send */
            if (select(1 + connected, NULL, &wfd, NULL, &tm) > 0)
            {
                int numsent;

                numsent = send(connected, sendBuffer_ptr, numread, 0);
                if (numsent == -1)
                    return;
                sendBuffer_ptr += numsent;
                numread        -= numsent;
            }
        } while (numread > 0);
    }
}
/* don't forget to close the file. */
fclose(sendFile);

Upvotes: 2

Related Questions