Sending html file with HTTP protocol via tcp and browser shows error
I am writing an HTTP webserver, when I send a text file with the equivalent HTML file content to the browser, the browser shows it correctly, but when I send the HTML file browser itself, it renders the HTML page to the second and then the "connection has been reset" message appears.
I noticed that the text file is larger than the HTML file, but I have no idea why
text size = 286 bytes
HTML size = 142 bytes
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's another piece of code that's 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;
source to share
You have two important problems that I see
-
The gaps are
send
wrong, this line (very important)int c=send(connected,(const char*)&sendBuffer,strlen(&writable[0]),0);
it should be
int c=send(connected,(const char*) sendBuffer,strlen(&writable[0]),0); /* ^ * No ampersand */
since the array
sendBuffer
decays to a pointer and you don't need that. -
You are also passing the first parameter
select
from the manualnfds - highest file descriptor number 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 use it after the call
send
, you have to call it before to see if it can be written to the file descriptor.
Your code is a little more complex for the task it developed, so I propose the following solution with fixed issues and improvement on some others
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 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 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);
source to share
Half answer. First, even if "Usage \n
works", it violates the standard. You need to use CRLF. Use CRLF. Period.
For the rest of the code. I doubt this will change many things, but I would restructure the code a bit. This is important for the send function.
Separated the data transfer to its own function. You might also consider splitting the dispatch header into its own function - if you can find a good way to structure it. When you expand to send text or html or such Etc. Etc. Etc., you have to finalize the title for your own function. Doing this early would be helpful.
Only means a rough 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;
}
source to share