HTTP separation of Response Header and non HTTP Data - c++

I'm just writing a little client side HTTP application. It just sends a GET Request to an IP Camera and then receives a Screenshot in jpeg format.
Now for the implementation of HTTP I am using Boost Asio. So for the first try I oriented pretty much at the sync_client example Boost Asio Sync HTTP Client Example.
Now mainly I'm a bit worried by the separation of Headers and Data.
First I get the first line of the response:
boost::asio::streambuf response;
boost::asio::read_until(*m_Socket, response, "\r\n");
std::istream response_stream(&response);
std::string http_version;
response_stream >> http_version;
if (!response_stream || http_version.substr(0, 5) != "HTTP/")
{
std::cout << "Invalid response\n";
return;
}
uint32_t status_code;
response_stream >> status_code;
std::string status_message;
std::getline(response_stream, status_message);
if (status_code != 200) // 200 = status code OK
{
std::cout << "Response returned with status code " << status_code << "\n";
return;
}
Now until here everything is clear to me.I'm reading until first new line and then check the stuff in my buffer.
Now I'm trying to read the second part of the header:
boost::asio::read_until(*m_Socket, response, "\r\n\r\n");
std::string header;
while (std::getline(response_stream, header) && (header != "\r"))
{
std::cout << header << "\n";
}
std::cout << "\n";
Now to this I have some questions:
The while loop is searching until there is a blank line ( the only line where a \r stands all by itself ). Now if I assume that a new line is defined by \r , why do I use \r\n\r\n at boost::asio::read_until ? I mean I would expect wether the one or the other, but both?
If I call the boost::asio::read_until method with \r\r as delimiter it throws an End of File exception. This stands in contrary to what my while loop is searching since this is looking for a \r\r ( since it looks line after line, and every line closes with a \r )
So as you can see Im quite worried how to divide stuff inside my header. It's getting even worser because the boost::asio::read_until call does always read further than the delmiter ( this is actually OK, since it's mentioned in the documentation ), but still it kinda always has the same trail of data ( from the actualy jpeg ), with the same length following.
Maybe someone could enlighten me?

'\r' is the carriage return (CR) character and '\n' is the line-feed (LF) character. HTTP message lines are terminated by "\r\n" (CRLF).
From "HTTP The Definitive Guide":
It is worth pointing out that while the HTTP specification for
terminating lines is CRLF, robust application also should accept just
a line-feed character. Some older or broken HTTP applications do not
always send both the carriage return and line feed.
What seems to be throwing you off is that some line I/O functions (in this case std::getline()) automatically strip the trailing '\n' so you are only seeing the preceding '\r'. I think what you should be doing, is looking for a blank line (rather than a line with only '\r'). And a line that is only '\r' should be considered a blank line.

The "\r\n\r\n" delimiter is used to separate the headers from the actual server's response(which is the JPEG image you are fetching). That's why you're using it; headers are separated by "\r\n".
You should read until "\r\n\r\n". Everything that comes afterwards is the JPEG image. Note that you can guess the length of the file by checking the Content-Length header, or just read until the server closes the socket.

Related

Smtp client hangs after sending data

I want to build an stmp client using c++ for learning purposes.
After I managed to implement the initial connection + auth login I am stuck on sending the message after using the data command.
Here is my code
void sendmail()
{
write_command("MAIL FROM: <foo#bar.de>");
write_command("RCPT TO: <bar.foo#baz.de>");
write_command("DATA");
write_command("Subject: testmail"); // HANGS here after data command
write_command("BlaBlub");
write_command(" ");
write_command(".");
write_command("QUIT");
}
void write_command(std::string command)
{
ssize_t n;
empty_buffer();
command += '\r';
command += '\n';
char command_buffer[255];
strcpy(command_buffer, command.c_str());
n = write(sockfd,command_buffer,strlen(command_buffer));
if (n < 0){
error("ERROR writing to socket");
}
n = read_to_buffer();
if (n < 0) {
error("ERROR reading from socket");
}
printf("%s\n",this->buffer);
}
I'm using smtp.mailtrap.io on port 25.
Here is a gist with the full class https://gist.github.com/xhallix/7f2d87a8b2eab4953d161059c2482b37
Here is the server output
Starting smpt client
220 mailtrap.io ESMTP ready
250-mailtrap.io
250-SIZE 5242880
250-PIPELINING
250-ENHANCEDSTATUSCODES
250-8BITMIME
250-DSN
250-AUTH PLAIN LOGIN CRAM-MD5
250 STARTTLS
334 VXNlcm5hbWU6
334 UGFzc3dvcmQ6
235 2.0.0 OK
250 2.1.0 Ok
250 2.1.0 Ok
354 Go ahead
(HANGS HERE)
Thanks for helping me out
DATA command expects the whole mail message, as shown here. The write_command() sends a message by lines and expects response after each line. Since the server returns the response once the mail message is finished (after empty line and dot), it stays in the hanging mode after the first message line. This code snippet can be helpful for your case.
BTW, you should put an empty line between the mail header and body, which I guess is after the subject line. Also, it might happen that the server rejects the message without the From and To headers.

(Qt) Curl uploading to smtp sends an empty email

I'm using Qt and I recently made a similar application using gmail. Now, I want to send the email from outlook to gmail. EDIT: I just tried sending from outlook to outlook using an app password but still empty email in my outlook inbox... END EDIT Here is my code:
if(file.open(QIODevice::ReadWrite)){ //Writes in the msg.txt
QTextStream stream(&file);
stream << "From: \"Me\" <xxxxxxxxxx#outlook.com>" << endl;
stream << "To: \"Me\" <xxxxxxxxxxxx#gmail.com>" << endl;
stream << "Subject: Subject" << endl;
stream << msg << endl; //msg is just a QString variable
}
QString cmd = "ccurl smtp://smtp-mail.outlook.com:587 -v --mail-from \"xxxxxxxxxxxx#outlook.com\" --mail-rcpt \"xxxxxxxxxxxx#gmail.com\" --ssl -u xxxxxxxxxxxxxx#outlook.com:xxxxxxxxxxxxxx -T \"msg.txt\" -k --anyauth --insecure & pause";
const std::string s = cmd.toStdString();
const char* ccmd = s.c_str();
system(ccmd);
Pause is just used for testing purposes. Also, my .exe is named 'ccurl' and the console that appears doesn't throw any error. I do receive an email but it just says something like (Empty)
---
Email checked by avast....
Thanks for your help!
Ps. Don't tell me to use libcurl instead
You are missing an empty line between the end of the headers and the start of the message body. Without it, the rest of the message is interpreted as if it was still part of the headers.
Also, endl forces a flush in the stream for no good reason, which kills performance when done on files. Just use \n.

Force download on Mongoose Server

I'm developing a RESTful API using Mongoose Web Server. I'm sending a file using
mg_send_file(conn, path, NULL);
but if the file is plain text, or a PDF, it just displays in the browser, instead of forcing the download, which is what I need.
How can I achieve that?
Thanks
--- Update:
I also tried to use
const char* extraHeaders = "Content-Disposition: attachment;
filename=somefilename.txt";
mg_send_file(conn, "somefilename.txt", extraHeaders);
return MG_MORE;
but the connection keeps running, nothing happens.
Final solution was:
const char* extraHeaders = "Content-Disposition: attachment;
filename=\"somefilename.txt\"\r\n";
mg_send_file(conn, "somefilename.txt", extraHeaders);
return MG_MORE;
Note the filename between "", and the \r\n at the end of any extra header.

Make this line pylint correct

if(re.search("USN:.*MediaRenderer", datagram, flags=re.IGNORECASE)):
deviceXML = re.search("LOCATION:(.*.xml)", datagram, flags=re.IGNORECASE).group(1)
I am getting pytlint error "line too long" for 2nd line
How to correct this ?
Cut your line to meet the length (80 chars usually):
if(re.search("USN:.*MediaRenderer", datagram, flags=re.IGNORECASE)):
deviceXML = re.search("LOCATION:(.*.xml)",
datagram,
flags=re.IGNORECASE).group(1)
Or use your own customization of pylint.

Separating HTTP Response Body from Header in C++

I'm currently writing my own C++ HTTP class for a certain project. And I'm trying to find a way to separate the response body from the header, because that's the only part I need to return.
Here's a sample of the raw http headers if you're not familiar with it:
HTTP/1.1 200 OK
Server: nginx/0.7.65
Date: Wed, 29 Dec 2010 06:13:07 GMT
Content-Type: text
Connection: keep-alive
Vary: Cookie
Content-Length: 82
Below that is the HTML/Response body. What would be the best way to do this? I'm only using Winsock library for the requests by the way (I don't even think this matters).
Thanks in advance.
HTTP headers are terminated by the sequence \r\n\r\n (a blank line). Just search for that, and return everything after. (It may not exist of course, e.g. if it was in response to a HEAD request.)
Do you need to roll your own? There are C/C++ libraries out there for doing HTTP, e.g. libcurl. If you need to support the full gamut of HTTP, then it's not always a simple delineation. You might also have to cater, for example, for chunked encoding.
DO IF Socket.IsServerReady(Sock) THEN Text = text + Socket.Read(Sock, 65000) 'print text '' 32000 bytes... whatever they give us Bytes = bytes + Socket.Transferred StatusBar.Panel(0).Caption = "Bytes Read: " + STR$(Bytes)
END IF
'RichEdit.addstrings text zzz=Bytes LOOP UNTIL Socket.Transferred = 0 RichEdit.Clear RichEdit.Text = text Socket.Close(Sock) dim mem as qmemorystream dim S$ as string S$ = text for n=0 to 400 buff$=mid$(S$,n,5)
if buff$="alive" then' found end of headers richedit1.addstrings (buff$) richedit1.addstrings (mid$(S$,n,9)) richedit1.addstrings str$(n+9) zzz=n+8'offset + 8 bit space after headers and before Bof end if next n Mem.WriteStr(S$, LEN(S$))'write entire file to memory Mem.Position = zzz ' use offset as Start position S$ = Mem.ReadStr(LEN(S$)) ' read rest of file into string till Eof Mem.Close' dont forget to close 'PRINT S$ '' print it
Filex.Open("c:/CAP.AVI", fmCreate)'create file on system filex.WriteBinStr(S$,len(S$)-zzz)' write to it filex.close 'dont forget to close