range downloads in http - c++

I need to download a html page in chunks. I had build a GET reuest whick can download a certain range of data. But i am unsuccessful in doing this in a repetitive manner.
Basically I have to reciver first 0-99 bytes then 100-199 and so on...
Also I would be grateful to know how toh know the exact size of receiving file beforehand using c or c++ code.
Following is my code.
i have exempted connectig to sockets etc. as it have been done successfully.
int c=0,s=0;
while(1)
{
get = build_get_query(host, page,s);
c+=1;
fprintf(stderr, "Query is:\n<<START>>\n%s<<END>>\n", get);
//Send the query to the server
int sent = 0;
cout<<"sending "<<c<<endl;
while(sent < strlen(get))
{
tmpres = send(sock, get+sent, strlen(get)-sent, 0);
if(tmpres == -1)
{
perror("Can't send query");
exit(1);
}
sent += tmpres;
}
//now it is time to receive the page
memset(buf, 0, sizeof(buf));
int htmlstart = 0;
char * htmlcontent;
cout<< "reciving "<<c<<endl;
while((tmpres = recv(sock, buf, BUFSIZ, 0)) > 0)
{
if(htmlstart == 0)
{
/* Under certain conditions this will not work.
* If the \r\n\r\n part is splitted into two messages
* it will fail to detect the beginning of HTML content
*/
htmlcontent = strstr(buf, "\r\n\r\n");
if(htmlcontent != NULL)
{
htmlstart = 1;
htmlcontent += 4;
}
}
else
{
htmlcontent = buf;
}
if(htmlstart)
{
fprintf(stdout, htmlcontent);
}
memset(buf, 0, tmpres);
}
if(tmpres < 0)
{
perror("Error receiving data");
}
s+=100;
if(c==5)
break;
}
char *build_get_query(char *host, char *page,int i)
{
char *query;
char *getpage = page;
int j=i+99;
char tpl[100] = "GET /%s HTTP/1.1\r\nHost: %s\r\nRange: bytes=%d-%d\r\nUser- Agent: %s\r\n\r\n";
if(getpage[0] == '/')
{
getpage = getpage + 1;
fprintf(stderr,"Removing leading \"/\", converting %s to %s\n", page, getpage);
}
query = (char *)malloc(strlen(host)+strlen(getpage)+8+strlen(USERAGENT)+strlen(tpl)-5);
sprintf(query, tpl, getpage, host, i , j, USERAGENT);
return query;
}

Also I would be grateful to know how toh know the exact size of receiving file beforehand using c or c++ code.
If the server supports a range request to the specific resource (which is not guaranteed) then the answer will look like this:
HTTP/1.1 206 partial content
Content-Range: bytes 100-199/12345
This means that the response will contain the bytes 100..199 and that the total size of the content is 12345 bytes.
There are lots of questions here which deal with parsing HTTP headers so I will not go into the detail on how to specifically use C/C++ to extract these data from the header.
Please note also that you are doing a HTTP/1.1 request and thus must deal with possible chunked responses and implicit keep alive. I really recommend to use existing HTTP libraries instead of doing it all by hand and doing it wrong. If you really want to implement it all by your own please study the specification of HTTP.

Related

Visual C++ 19.10.25019 – C++ compiler bug?

I have a function for receiving messages of variable length through TCP. The send-function creates a buffer, puts the length of message in first four bytes, fills the rest with the message, and sends by parts. But the receive-function was receiving 4 bytes less. And suddenly, when I put one printf, everything is working as it should.
bool TCP_Server::recvMsg(SOCKET client_sock, std::unique_ptr<char[]>& buf_ptr, int* buf_len)
{
int msg_len;
int rcvd = 0, tmp;////
/* get msg len */
if((tmp = recv(client_sock, (char*)&msg_len, sizeof(msg_len), 0)) == -1)
{
handle_error("recv");
return false;
}
*buf_len = msg_len;
printf("msg_len = %d\n", msg_len); //
printf("tmp getting msg_len = %d\n", tmp);//
rcvd += tmp;//
buf_ptr.reset((char*)malloc(msg_len));
if(buf_ptr.get() == nullptr) // not enough memory
{
handle_error("malloc");
return false;
}
/* get msg of specified len */
/* get by biggest available pieces */
int i = 1;
while(int(msg_len - 1440 * i) > 0)
{
char* cur_ptr = buf_ptr.get() + 1440 * (i - 1);
if((tmp=recv(client_sock, cur_ptr, 1440, 0)) == -1)
{
handle_error("recv");
return false;
}
printf("1440 = %d\n", tmp); // doesn't work if I comment this line
rcvd += tmp;
i++;
}
int rest = msg_len - 1440 * (i - 1);
/* get the rest */
if((tmp = recv(client_sock, buf_ptr.get() + msg_len - rest, rest, 0)) == -1)
{
handle_error("(recv)reading with msg_len");
return false;
}
rcvd += tmp;//
printf("rcvd = %d\n", rcvd);//
return true;
}
In sum, if I comment printf("1440 = %d\n", tmp);, the function is receiving 4 bytes less.
I'm compiling with x86 Debug.
Here's the dissimilar lines in asm(/FA flag): http://text-share.com/view/50743a5e
But I don't see anything suspicious
printf writes to the console, which is a fairly slow operation, relatively speaking. The extra delay it produces might easily change how much data has arrived in the buffer when you call recv.
As Tulon comments, reads from TCP streams can be any length. TCP doesn't preserve message boundaries, so they don't necessarily match the send sizes on the other end. And if less data has been sent across the network than you asked to read, you'll get what is available.
Solution: stop thinking of 1440 byte chunks. Get rid of i and simply compare rcvd to msg_len.

Using c++ to send smtp mail

I was wondering if you could help me understand what is going wrong here. I am trying to write a little client that engages in SMTP dialogue on port 25.
If you recall SMTP, there's a few things you need to send, and then you write the email after the DATA message, ending with a period on it's own line to send the email.
There is something problematic in the way the program handles this. It handles the dialogue fine until after the DATA message. It will recognize the period only if I type it first. After any subsequent line, any code execution seems to be lost. The if statement fails to recognize if a period has been entered. Thank you again. Attached is the relevant code..
void readstuff(int sock, char* buf) {
int r = read (sock, buf, BUFSIZE -1);
buf[r] = NULL;
cout << buf << endl;
}
void doit(int sock, string arg, char* buf) {
int r = write(sock, arg.c_str(), arg.length());
readstuff(sock, buf);
}
int main(int argc, char *argv[]) {
char buf[BUFSIZE];
// Make a socket
int sock = MakeSocket(argv[1], argv[2]);
cout << "socket is " << sock << endl;
assert(sock != -1);
// Begin dialogue
doit(sock, "HELO " + org.substr(org.find("#") + 1) + "\r\n", buf);
doit(sock, "MAIL FROM: <" + org + "> \r\n", buf);
doit(sock, "RCPT TO: <" + dest + "> \r\n", buf);
doit(sock, "DATA \r\n", buf);
readstuff(sock, buf); //should say "go ahead"
//User writes email here
while (true) {
string line = "";
getline(cin, line);
doit(sock, line + "\r\n", buf);
if (line == ".") {
readstuff(sock, buf); //should say "email cleared to send"
return 0;
}
}
}
Please read the SMTP specification, RFC 5321, particularly sections 4.1.1.4 DATA and 4.5.2 Transparency:
4.1.1.4. DATA (DATA)
The receiver normally sends a 354 response to DATA, and then treats
the lines (strings ending in <CRLF> sequences, as described in
Section 2.3.7) following the command as mail data from the sender.
This command causes the mail data to be appended to the mail data
buffer. The mail data may contain any of the 128 ASCII character
codes, although experience has indicated that use of control
characters other than SP, HT, CR, and LF may cause problems and
SHOULD be avoided when possible.
The mail data are terminated by a line containing only a period, that
is, the character sequence "<CRLF>.<CRLF>", where the first <CRLF> is
actually the terminator of the previous line (see Section 4.5.2).
This is the end of mail data indication. The first <CRLF> of this
terminating sequence is also the <CRLF> that ends the final line of
the data (message text) or, if there was no mail data, ends the DATA
command itself (the "no mail data" case does not conform to this
specification since it would require that neither the trace header
fields required by this specification nor the message header section
required by RFC 5322 [4] be transmitted). An extra <CRLF> MUST NOT
be added, as that would cause an empty line to be added to the
message. The only exception to this rule would arise if the message
body were passed to the originating SMTP-sender with a final "line"
that did not end in <CRLF>; in that case, the originating SMTP system
MUST either reject the message as invalid or add <CRLF> in order to
have the receiving SMTP server recognize the "end of data" condition.
The custom of accepting lines ending only in <LF>, as a concession to
non-conforming behavior on the part of some UNIX systems, has proven
to cause more interoperability problems than it solves, and SMTP
server systems MUST NOT do this, even in the name of improved
robustness. In particular, the sequence "<LF>.<LF>" (bare line
feeds, without carriage returns) MUST NOT be treated as equivalent to
<CRLF>.<CRLF> as the end of mail data indication.
Receipt of the end of mail data indication requires the server to
process the stored mail transaction information. This processing
consumes the information in the reverse-path buffer, the forward-path
buffer, and the mail data buffer, and on the completion of this
command these buffers are cleared. If the processing is successful,
the receiver MUST send an OK reply. If the processing fails, the
receiver MUST send a failure reply. The SMTP model does not allow
for partial failures at this point: either the message is accepted by
the server for delivery and a positive response is returned or it is
not accepted and a failure reply is returned. In sending a positive
"250 OK" completion reply to the end of data indication, the receiver
takes full responsibility for the message (see Section 6.1). Errors
that are diagnosed subsequently MUST be reported in a mail message,
as discussed in Section 4.4.
When the SMTP server accepts a message either for relaying or for
final delivery, it inserts a trace record (also referred to
interchangeably as a "time stamp line" or "Received" line) at the top
of the mail data. This trace record indicates the identity of the
host that sent the message, the identity of the host that received
the message (and is inserting this time stamp), and the date and time
the message was received. Relayed messages will have multiple time
stamp lines. Details for formation of these lines, including their
syntax, is specified in Section 4.4.
Additional discussion about the operation of the DATA command appears
in Section 3.3.
Syntax:
data = "DATA" CRLF
4.5.2. Transparency
Without some provision for data transparency, the character sequence
"<CRLF>.<CRLF>" ends the mail text and cannot be sent by the user.
In general, users are not aware of such "forbidden" sequences. To
allow all user composed text to be transmitted transparently, the
following procedures are used:
o Before sending a line of mail text, the SMTP client checks the
first character of the line. If it is a period, one additional
period is inserted at the beginning of the line.
o When a line of mail text is received by the SMTP server, it checks
the line. If the line is composed of a single period, it is
treated as the end of mail indicator. If the first character is a
period and there are other characters on the line, the first
character is deleted.
...
Your DATA command needs to account for that:
Your doit() function is expecting a response after sending a string. That is the wrong logic to use while sending the email data. You can't read a response until after the final terminating <CRLF>.<CRLF> has been sent.
you have to apply transparency to any line in the email that begins with a . character.
With that said, try something more like this:
int readLine(int sock, string &line)
{
// read a line from sock until CRLF is reached.
// I leave this as an exercise for you to implement...
line = ...;
return -1 on error, else 0;
}
int readResponse(int sock)
{
// Please read RFC 5321 section 4.2 for the PROPER format
// of an SMTP response. You should be reading from the
// socket until you receive the terminating
// "Reply-code [ SP textstring ] CRLF" line...
string line;
int r = readLine(sock, line);
if (r < 0) return r;
string code = line.substr(0, 3);
string text = line.substr(4);
if ((line.length() >= 4) && (line[3] = '-'))
{
do
{
r = readLine(sock, line);
if (r < 0) return r;
text += (" " + line.substr(4));
}
while (line.compare(0, 4, code+"-") == 0);
}
cout << code << ": " << text << endl;
return stoi(code);
}
int sendText(int sock, const string &arg)
{
const char *p = arg.c_str();
int len = arg.length();
while (len > 0)
{
int r = write(sock, p, len);
if (r <= 0) return -1;
p += r;
len -= r;
}
return 0;
}
int sendCmd(int sock, const string &arg)
{
int r = sendText(sock, arg + "\r\n");
if (r < 0) return r;
return readResponse(sock);
}
int main(int argc, char *argv[])
{
// Make a socket
int sock = MakeSocket(argv[1], argv[2]);
cout << "socket is " << sock << endl;
assert(sock != -1);
// Begin dialogue
// read the server greeting first...
if (readResponse(sock) != 220) {
// failed, do something...
}
if (sendCmd(sock, "HELO " + org.substr(org.find("#") + 1)) != 250) {
// failed, do something...
}
if (sendCmd(sock, "MAIL FROM: <" + org + ">") != 250) {
// failed, do something...
}
int r = sendCmd(sock, "RCPT TO: <" + dest + ">");
if ((r != 250) && (r != 251)) {
// failed, do something...
}
if (sendCmd(sock, "DATA") != 354) {
// failed, do something...
}
//User writes email here
while (true) {
string line;
getline(cin, line);
// A line consisting of only "." is a valid line in an email
// message, so you should not use that as a terminator in your
// input, use something else, like an EOF marker, or CTRL-C,
// or something...
if (some termination condition)
break;
// DO NOT call readResponse() here!
if (!line.empty() && (line[0] == '.')) {
if (sendText(sock, ".") < 0) {
// failed, do something...
}
}
if (sendText(sock, line) < 0) {
// failed, do something...
}
if (sendText(sock, "\r\n") < 0) {
// failed, do something...
}
}
// NOW call readResponse() here!
if (sendCmd(sock, ".") != 250) {
// failed, do something...
}
sendCmd(sock, "QUIT");
close(sock);
return 0;
}

How to properly delimit multiple images before sending them over a socket

let's say I need to send, for instance, five images from a client to a server over a socket and that I want to do it at once (not sending one and waiting for an ACK).
Questions:
I'd like to know if there are some best practices or guidelines for delimiting the end of each one.
What would be the safest approach for detecting the delimiters and processing each image once in the server? (In C/C++ if possible)
Thanks in advance!
Since images are binary data, it would be difficult to come up with delimiter that cannot be contained in the image. (And ultimately confusing the receiving side)
I would advice you to create a header that would be placed at the beginning of the transmission, or at the beginning of each image.
An example:
struct Header
{
uint32_t ImageLength;
// char ImageName[128];
} __attribute__(packed);
The sender should prepend this before each image and fill in the length correctly. The receiver would then know when the image ends and would expect another Header structure at that position.
The attribute(packed) is a safety, that makes sure the header will have the same alignment even if you compile server and client with different GCC versions. It's recomended in cases where structures are interpreted by different processes.
Data Stream:
Header
Image Data
Header
Image Data
Header
Image Data
...
You can use these function to send files (from client in java) to a server (in C). The idea is to send 4 bytes which indicates the file's size followed by the file content, when all files have been sent, send 4 bytes (all set to 0 zero) to indicate the end of the transfer.
// Compile with Microsoft Visual Studio 2008
// path, if not empty, must be ended with a path separator '/'
// for example: "C:/MyImages/"
int receiveFiles(SOCKET sck, const char *pathDir)
{
int fd;
long fSize=0;
char buffer[8 * 1024];
char filename[MAX_PATH];
int count=0;
// keep on receiving until we get the appropiate signal
// or the socket has an error
while (true)
{
if (recv(sck, buffer, 4, 0) != 4)
{
// socket is closed or has an error
// return what we've received so far
return count;
}
fSize = (int) ((buffer[0] & 0xff) << 24) |
(int) ((buffer[1] & 0xff) << 16) |
(int) ((buffer[2] & 0xff) << 8) |
(int) (buffer[3] & 0xff);
if (fSize == 0)
{
// received final signal
return count;
}
sprintf(filename, "%sIMAGE_%d.img", pathDir, count+1);
fd = _creat(filename, _S_IREAD | _S_IWRITE);
int iReads;
int iRet;
int iLeft=fSize;
while (iLeft > 0)
{
if (iLeft > sizeof(buffer)) iReads = sizeof(buffer);
else iReads=iLeft;
if ((iRet=recv(sck, buffer, iReads, 0)) <= 0)
{
_close(fd);
// you may delete the file or leave it to inspect
// _unlink(filename);
return count; // socket is closed or has an error
}
iLeft-=iRet;
_write(fd, buffer, iRet);
}
count++;
_close(fd);
}
}
The client part
/**
* Send a file to a connected socket.
* <p>
* First it send the file size if 4 bytes then the file's content.
* </p>
* <p>
* Note: File size is limited to a 32bit signed integer, 2GB
* </p>
*
* #param os
* OutputStream of the connected socket
* #param fileName
* The complete file's path of the image to send
* #throws Exception
* #see {#link receiveFile} for an example on how to receive the file from the other side.
*
*/
public void sendFile(OutputStream os, String fileName) throws Exception
{
// File to send
File myFile = new File(fileName);
int fSize = (int) myFile.length();
if (fSize == 0) return; // No empty files
if (fSize < myFile.length())
{
System.out.println("File is too big'");
throw new IOException("File is too big.");
}
// Send the file's size
byte[] bSize = new byte[4];
bSize[0] = (byte) ((fSize & 0xff000000) >> 24);
bSize[1] = (byte) ((fSize & 0x00ff0000) >> 16);
bSize[2] = (byte) ((fSize & 0x0000ff00) >> 8);
bSize[3] = (byte) (fSize & 0x000000ff);
// 4 bytes containing the file size
os.write(bSize, 0, 4);
// In case of memory limitations set this to false
boolean noMemoryLimitation = true;
FileInputStream fis = new FileInputStream(myFile);
BufferedInputStream bis = new BufferedInputStream(fis);
try
{
if (noMemoryLimitation)
{
// Use to send the whole file in one chunk
byte[] outBuffer = new byte[fSize];
int bRead = bis.read(outBuffer, 0, outBuffer.length);
os.write(outBuffer, 0, bRead);
}
else
{
// Use to send in a small buffer, several chunks
int bRead = 0;
byte[] outBuffer = new byte[8 * 1024];
while ((bRead = bis.read(outBuffer, 0, outBuffer.length)) > 0)
{
os.write(outBuffer, 0, bRead);
}
}
os.flush();
}
finally
{
bis.close();
}
}
To send the files from the client:
try
{
// The file name must be a fully qualified path
sendFile(mySocket.getOutputStream(), "C:/MyImages/orange.png");
sendFile(mySocket.getOutputStream(), "C:/MyImages/lemmon.png");
sendFile(mySocket.getOutputStream(), "C:/MyImages/apple.png");
sendFile(mySocket.getOutputStream(), "C:/MyImages/papaya.png");
// send the end of the transmition
byte[] buff = new byte[4];
buff[0]=0x00;
buff[1]=0x00;
buff[2]=0x00;
buff[3]=0x00;
mySocket.getOutputStream().write(buff, 0, 4);
}
catch (Exception e)
{
e.printStackTrace();
}
If you cannot easily send a header containing the length, use some likely delimiter. If the images are not compressed and consist of bitmap-stype data, maybe 0xFF/0XFFFF/0xFFFFFFF as fully-saturated luminance values are usually rare?
Use an escape-sequence to eliminate any instances of the delimiter that turn up inside your data.
This does mean iterating all the data at both ends, but depending on your data flows, and what is being done anyway, it may be a useful solution :(

C++ Sending Image via HTTP

I try to implement a simple HTTP server using C++. I was able to send text response to browser, but I am failing to send response for binary file request.
Here is my code to get HTML response to PNG file request:
string create_html_output_for_binary(const string &full_path)
{
const char* file_name = full_path.c_str();
FILE* file_stream = fopen(file_name, "rb");
string file_str;
size_t file_size;
if(file_stream != nullptr)
{
fseek(file_stream, 0, SEEK_END);
long file_length = ftell(file_stream);
rewind(file_stream);
// Allocate memory. Info: http://www.cplusplus.com/reference/cstdio/fread/?kw=fread
char* buffer = (char*) malloc(sizeof(char) * file_length);
if(buffer != nullptr)
{
file_size = fread(buffer, 1, file_length, file_stream);
stringstream out;
for(int i = 0; i < file_size; i++)
{
out << buffer[i];
}
string copy = out.str();
file_str = copy;
}
else
{
printf("buffer is null!");
}
}
else
{
printf("file_stream is null! file name -> %s\n", file_name);
}
string html = "HTTP/1.1 200 Okay\r\nContent-Type: text/html; charset=ISO-8859-4 \r\n\r\n" + string("FILE NOT FOUND!!");
if(file_str.length() > 0)
{
// HTTP/1.0 200 OK
// Server: cchttpd/0.1.0
// Content-Type: image/gif
// Content-Transfer-Encoding: binary
// Content-Length: 41758
string file_size_str = to_string(file_str.length());
html = "HTTP/1.1 200 Okay\r\nContent-Type: image/png; Content-Transfer-Encoding: binary; Content-Length: " + file_size_str + ";charset=ISO-8859-4 \r\n\r\n" + file_str;
printf("\n\nHTML -> %s\n\nfile_str -> %ld\n\n\n", html.c_str(), file_str.length());
}
return html;
}
This code successfully read file and store data on char* buffer.
What makes me confuse is the file_str always contains \211PNG, although when I check its size, is much large than \211PNG.
I suspect this is the problem that cause my image does not loaded in browsers because when I printf the html, it only shows:
HTTP/1.1 200 Okay
Content-Type: image/png; Content-Transfer-Encoding: binary; Content-Length: 187542;charset=ISO-8859-4
\211PNG
What I am thinking is the way to send binary data to browser is same with sending text data, so I make the string header first, then read file data, convert it to string and combine with the header, then finally send a large single string to HTTP socket.
I also tried this code:
if (file_stream != NULL)
{
short stringlength = 6;
string mystring;
mystring.reserve(stringlength);
fseek(file_stream , 0, SEEK_SET);
fread(&mystring[0], sizeof(char), (size_t)stringlength, file_stream);
printf("TEST -> %s, length -> %ld\n", mystring.c_str(), mystring.length());
fclose(file_stream );
}
But the HTML output always the same, and mystring also contains only \211PNG when printf-ed.
Am I in the wrong path?
Please help find out the mistakes in my code. Thank you.
You are storing the data in one large chunk into a std::stringstream. That will not work as the value zero will be interpreted as a null terminator. This causes everything after the null terminator to be ignored. You should use a container like std::vector to store and manage binary data.
#include <vector>
string create_html_output_for_binary(const string &full_path)
{
std::vector<char> buffer;
//... other code here
if(ile_stream != nullptr)
{
fseek(file_stream, 0, SEEK_END);
long file_length = ftell(file_stream);
rewind(file_stream);
buffer.resize(file_length);
file_size = fread(&buffer[0], 1, file_length, file_stream);
}
// .... other code here
}
To output the data do not use printf. It may handle new lines differently and will stop at the first null terminator is encounters. Instead (keeping with your use of C stream IO) use fwrite.
fwrite (buffer.data(), 1 , buffer.size() , stdout );
In order for the above to work you will need to reopen the stdout to that it writes in binary mode. This answer here on Stackoverflow shows how to accomplish that. This is just to output the contents to the stdout Since you are transmitting the date over sockets you do not have to do anything to stdout.
First, you need to open the file(where your picture is) to read binary.
fp=fopen(filename,"rb");
Next,set stdout to binary mode with this command:
_setmode(_fileno(stdout),O_BINARY);
You need to include <fcntl.h> and <io.h> headers.Find the exact size of the picture
you need to send,for example like Captain Obvilous has written:
fseek(fp, 0L, SEEK_END);
long file_length = ftell(fp);
rewind(fp);
Now use fread function to read all bytes from the picture:
while (!feof(fp))
{
fread(&ch, 1, 1, fp);
cout << ch;
}
Variable ch is type char.When you are finish set file mode back:
_setmode(_fileno(stdout), _O_TEXT);
**NOTE:**This code was written mostly in C but you can easily read files in C++ using istream

Receiving only necessary data with C++ Socket

I'm just trying to get the contents of a page with their headers...but it seems that my buffer of size 1024 is either too large or too small for the last packet of information coming through...I don't want to get too much or too little, if that makes sense. Here's my code. It's printing out the page just fine with all the information, but I want to ensure that it's correct.
//Build HTTP Get Request
std::stringstream ss;
ss << "GET " << url << " HTTP/1.0\r\nHost: " << strHostName << "\r\n\r\n";
std::string req = ss.str();
// Send Request
send(hSocket, req.c_str(), strlen(req.c_str()), 0);
// Read from socket into buffer.
do
{
nReadAmount = read(hSocket, pBuffer, sizeof pBuffer);
printf("%s", pBuffer);
}
while(nReadAmount != 0);
nReadAmount = read(hSocket, pBuffer, sizeof pBuffer);
printf("%s", pBuffer);
This is broken. You can only use the %s format specifier for a C-style (zero-terminated) string. How is printf supposed to know how many bytes to print? That information is in nReadAmount, but you don't use it.
Also, you call printf even if read fails.
The simplest fix:
do
{
nReadAmount = read(hSocket, pBuffer, (sizeof pBuffer) - 1);
if (nReadAmount <= 0)
break;
pBuffer[nReadAmount] = 0;
printf("%s", pBuffer);
} while(1);
The correct way to read an HTTP reply is to read until you have received a full LF-delimited line (some servers use bare LF even though the official spec says to use CRLF), which contains the response code and version, then keep reading LF-delimited lines, which are the headers, until you encounter a 0-length line, indicating the end of the headers, then you have to analyze the headers to figure out how the remaining data is encoded so you know the proper way to read it and know how it is terminated. There are several different possibilities, refer to RFC 2616 Section 4.4 for the actual rules.
In other words, your code needs to use this kind of structure instead (pseudo code):
// Send Request
send(hSocket, req.c_str(), req.length(), 0);
// Read Response
std::string line = ReadALineFromSocket(hSocket);
int rescode = ExtractResponseCode(line);
std::vector<std::string> headers;
do
{
line = ReadALineFromSocket(hSocket);
if (line.length() == 0) break;
headers.push_back(line);
}
while (true);
if (
((rescode / 100) != 1) &&
(rescode != 204) &&
(rescode != 304) &&
(request is not "HEAD")
)
{
if ((headers has "Transfer-Encoding") && (Transfer-Encoding != "identity"))
{
// read chunks until a 0-length chunk is encountered.
// refer to RFC 2616 Section 3.6 for the format of the chunks...
}
else if (headers has "Content-Length")
{
// read how many bytes the Content-Length header says...
}
else if ((headers has "Content-Type") && (Content-Type == "multipart/byteranges"))
{
// read until the terminating MIME boundary specified by Content-Type is encountered...
}
else
{
// read until the socket is disconnected...
}
}