I'm trying to write a HTTP Request library for practice with Sockets and C++ in general. I created a seperate function to setup the struct needed to Connect() however it is now breaking and I cant seem to figure out why even with a debugger. Also looking for any code optimization I can be doing better.
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include <string>
#include <vector>
#pragma comment (lib, "Ws2_32.lib")
/*
Struct For Holding Data Pre/Post HTTP Request
*/
struct HTTPRequest_s
{
std::string Host; // Host
std::string Path; // Path
short Port; // Port
short HTTPCode; // 404, 301..etc
std::string Response; // Holds Data We Get Back
std::vector<std::pair<std::string, std::string>> PostParams; // Holds Any Data We POST (If Applicable)
};
bool InitWinsock()
{
WSADATA WsaDat;
if (WSAStartup(MAKEWORD(2, 2), &WsaDat) != 0)
{
std::cout << "Failed To Start Winsocket With WSAStartup()\n";
WSACleanup();
return(false);
}
return(true);
}
/*
Recieve Everything Including Headers
(Note: Sometimes A Server Will Reply Without A 'Content-Length' Property)
*/
std::string loopRecieve(SOCKET Sock)
{
char recvBuf[256]; // For Transporting Data
std::string outBuf; // Output String
unsigned int nret;
while (true)
{
nret = recv(Sock, recvBuf, sizeof(recvBuf), 0);
if (nret == -1)
{
printf("A Socket Error Occured(-1)\n");
break;
}
else if (nret == 0)
{
printf("Done Reading!\n");
break;
}
// Append Newly Recieved Bytes To String
outBuf.append(recvBuf, nret);
}
return(outBuf);
}
/*
Create The Struct We Need For Connection
*/
void CreateAddrInfoStruct(HTTPRequest_s *HTTP_s, sockaddr_in *servAddr)
{
int nret;
ADDRINFO *pResult = NULL,
hints;
// Strip Out 'http://' && 'https://'
if (HTTP_s->Host.find("http://") != -1) {
HTTP_s->Host = HTTP_s->Host.substr(HTTP_s->Host.find("http://") + 7);
}
else if (HTTP_s->Host.find("https://") != -1) {
printf("SSL Not Supported Yet\n");
return;
}
hints = { 0 };
hints.ai_flags = AI_ALL;
hints.ai_family = PF_INET;
hints.ai_protocol = IPPROTO_IPV4;
nret = getaddrinfo(HTTP_s->Host.c_str(), nullptr, &hints, &pResult);
if (nret != 0)
{
std::cout << "Failed To Do GetAddrInfo()\n";
return;
}
servAddr = { 0 };
servAddr->sin_family = AF_INET;
servAddr->sin_addr.S_un.S_addr = *((ULONG*)&(((sockaddr_in*)pResult->ai_addr)->sin_addr));
servAddr->sin_port = htons(HTTP_s->Port);
}
/*
Basic GET
*/
void getWebPage(HTTPRequest_s *HTTP_s)
{
int nret;
SOCKET theSocket;
sockaddr_in servAddr;
// Create Our Struct
CreateAddrInfoStruct(HTTP_s, &servAddr);
theSocket = socket(AF_INET, SOCK_STREAM, 0);
if (theSocket == INVALID_SOCKET)
{
std::cout << "Socket Is Invalid, Is Winsock Initialized?\n";
return;
}
nret = connect(theSocket, (sockaddr*)&servAddr, sizeof(servAddr));
if (nret == SOCKET_ERROR)
{
std::cout << "Failed To Connect To Host\n";
return;
}
std::string request;
request = "GET " + HTTP_s->Path + " HTTP/1.1" + "\r\n";
request += "Host: " + HTTP_s->Host + "\r\n";
request += "Accept: */*\r\n";
request += "Accept-Language: en-us\r\n";
request += "Connection: close\r\n";
request += "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0\r\n";
request += "Referer: http://" + HTTP_s->Host + "\r\n";
request += "\r\n";
nret = send(theSocket, request.c_str(), request.length(), 0);
if (nret == SOCKET_ERROR)
{
std::cout << "Failed To Send To Host\n";
return;
}
HTTP_s->Response = loopRecieve(theSocket);
closesocket(theSocket);
}
/*
Basic POST
*/
void postWebPage(HTTPRequest_s *HTTP_s)
{
int nret;
SOCKET theSocket;
sockaddr_in servAddr;
// Create Our Struct
CreateAddrInfoStruct(HTTP_s, &servAddr);
theSocket = socket(AF_INET, SOCK_STREAM, 0);
if (theSocket == INVALID_SOCKET)
{
std::cout << "Socket Is Invalid, Is Winsock Initialized?\n";
return;
}
nret = connect(theSocket, (sockaddr*)&servAddr, sizeof(servAddr));
if (nret == SOCKET_ERROR)
{
std::cout << "Failed To Connect To Host\n";
return;
}
// Structure POST Data Properly
std::string concatPostData;
for (auto i : HTTP_s->PostParams)
{
concatPostData += i.first + "=" + i.second + "&";
}
concatPostData.pop_back(); // Pop Off Extra '&' At The End Of Loop (Note: Might Not Need To Pop This Off)
// Construct HEADER
std::string header;
header = "POST " + HTTP_s->Path + " HTTP/1.1\r\n";
header += "Host: " + HTTP_s->Host + ":" + std::to_string(HTTP_s->Port) + "\r\n";
header += "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0\r\n";
header += "Referer: http://" + HTTP_s->Host + "\r\n";
header += "Content-Type: application/x-www-form-urlencoded\r\n";
header += "Content-Length: " + std::to_string(concatPostData.length()) + "\r\n";
header += "Accept-Charset: utf-8\r\n";
header += "Connection: close\r\n\r\n";
header += concatPostData + "\r\n";
header += "\r\n";
// Need To Check Here That We Sent All Data
nret = send(theSocket, header.c_str(), header.length(), 0);
if (nret == SOCKET_ERROR)
{
std::cout << "Failed To Send To Host\n";
return;
}
HTTP_s->Response = loopRecieve(theSocket);
closesocket(theSocket);
}
int main()
{
// Init Winsock So We Can Use Sockets
if (InitWinsock() != true)
{
printf("Failed To Init Winsock!\n");
return(0);
}
// Setup HTTPRequest Structure
HTTPRequest_s Http_s;
Http_s.Host = "http://google.com";
Http_s.Path = "/";
Http_s.Port = 80;
// Make Our Request
getWebPage(&Http_s);
// Print The Data From Our Request Result
printf("Data Dump:\n%s", Http_s.Response.c_str());
system("pause");
return(0);
}