Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
boomanaiden154 committed Apr 23, 2020
0 parents commit 22cacf7
Show file tree
Hide file tree
Showing 4 changed files with 313 additions and 0 deletions.
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
test: test.cpp client.hpp
g++ -g test.cpp -o test
303 changes: 303 additions & 0 deletions client.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,303 @@
#pragma once

#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netdb.h>
#include <pthread.h>
#include <cstring>
#include <iostream>
#include <vector>
#include <string>
#include <map>

#define BUFFERSIZE 8192

class HTTPHeader
{
public:
std::multimap<std::string, std::string> headers;
bool isRequest;
std::string headerField1;
std::string headerField2;
std::string headerField3;
std::string body;
//parser state, so that if the header is in multiple packets it can still be parsed
std::string buffer;
int bufferIndex;
std::string fieldName;
std::string fieldValue;
bool parsingFieldName;
bool parsingHeader;

std::string requestType()
{
return headerField1;
}

std::string path()
{
return headerField2;
}

std::string protocol()
{
if(isRequest)
{
return headerField3;
}
else
{
return headerField1;
}
}

void setProtocol(std::string toSet)
{
if(isRequest)
{
headerField3 = toSet;
}
else
{
headerField1 = toSet;
}
}

std::string statusCode()
{
return headerField2;
}

void setStatusCode(std::string toSet)
{
headerField2 = toSet;
}

std::string status()
{
return headerField3;
}

void setStatus(std::string toSet)
{
headerField3 = toSet;
}

HTTPHeader(bool isRequest_ = false)
{
bufferIndex = 0;
parsingFieldName = true;
isRequest = isRequest_;
parsingHeader = true;
}

void parse(std::string input)
{
if(parsingHeader)
{
buffer += input;
if(bufferIndex == 0)
{
while(buffer[bufferIndex] != ' ')
{
headerField1 += buffer[bufferIndex];
bufferIndex++;
}
bufferIndex++;
while(buffer[bufferIndex] != ' ')
{
headerField2 += buffer[bufferIndex];
bufferIndex++;
}
bufferIndex++;
while(buffer[bufferIndex] != '\r')
{
headerField3 += buffer[bufferIndex];
bufferIndex++;
}
bufferIndex += 2;
}
while(buffer[bufferIndex] != '\r' && buffer[bufferIndex + 1] != '\n')
{
if(parsingFieldName)
{
while(buffer[bufferIndex] != ':' && buffer[bufferIndex + 1] != ' ')
{
fieldName += buffer[bufferIndex];
bufferIndex++;
}
bufferIndex += 2;
parsingFieldName = false;
}
else
{
while(buffer[bufferIndex] != '\r' && buffer[bufferIndex + 1] != '\n')
{
fieldValue += buffer[bufferIndex];
bufferIndex++;
}
bufferIndex += 2;
headers.insert(std::pair<std::string,std::string>(fieldName,fieldValue));
fieldName.clear();
fieldValue.clear();
parsingFieldName = true;
}
}
bufferIndex += 2; //for final \r\n
body += buffer.substr(bufferIndex, buffer.size() - bufferIndex);
parsingHeader = false;
}
else
{
body += input;
}
}

std::string getHeaderString()
{
std::string toReturn;
if(headerField1.size() != 0 && headerField2.size() != 0 && headerField3.size() != 0)
{
toReturn += headerField1 + " " + headerField2 + " " + headerField3 + "\r\n";
}
std::map<std::string, std::string>::iterator itr;
for(itr = headers.begin(); itr != headers.end(); itr++)
{
toReturn += itr->first + ": " + itr->second + "\r\n";
}
if(toReturn.size() != 0)
{
toReturn += "\r\n";
}
toReturn += body;
return toReturn;
}
};

class HTTPClient
{
public:
struct uri
{
std::string protocol;
std::string domain;
std::string path;
int port;
};

static struct uri parseURI(std::string uri)
{
struct uri toReturn;
toReturn.port = -1;
int index = 0;
while(uri[index] != ':')
{
toReturn.protocol += uri[index];
index++;
}
index += 3;
while(uri[index] != ':' && uri[index] != '/' && index < uri.size())
{
toReturn.domain += uri[index];
index++;
}
if(uri[index] == ':')
{
index++;
std::string portTemp;
while(uri[index] != '/' && index < uri.size())
{
portTemp += uri[index];
index++;
}
toReturn.port = std::stoi(portTemp);
}
while(index < uri.size())
{
toReturn.path += uri[index];
index++;
}
return toReturn;
}

static int initalizeSocket(std::string address, int port)
{
int sockfd;

struct addrinfo hints;
struct addrinfo* servinfo;
struct addrinfo* p;

memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;

getaddrinfo(address.c_str(), std::to_string(port).c_str(), &hints, &servinfo);

for(p = servinfo; p != NULL; p = p->ai_next)
{
if((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
{
continue;
}
if(connect(sockfd, p->ai_addr, p->ai_addrlen) == -1)
{
close(sockfd);
continue;
}
break;
}

if(p == NULL)
{
return -1;
}

freeaddrinfo(servinfo);

return sockfd;
}

static void getFile(std::string uri)
{
struct uri websiteURI = parseURI(uri);
int port = websiteURI.port == -1 ? 80 : websiteURI.port;
int sockfd = initalizeSocket(websiteURI.domain, port);
std::string path = websiteURI.path == "" ? "/" : websiteURI.path;

HTTPHeader request(true);
request.headerField1 = "GET";
request.headerField2 = "/";
request.headerField3 = "HTTP/1.1";

request.headers.insert(std::pair<std::string,std::string>("Host",websiteURI.domain));
request.headers.insert(std::pair<std::string,std::string>("User-Agent","custom/0.0.1"));
request.headers.insert(std::pair<std::string,std::string>("Accept","*/*"));

std::string headerString = request.getHeaderString();

send(sockfd, headerString.c_str(), headerString.size(), 0);

HTTPHeader response;

char headerBuffer[8192];

int headerRecieved = recv(sockfd, headerBuffer, 8191, 0);
headerBuffer[headerRecieved + 1] = '\0';

response.parse(std::string(headerBuffer));

while(response.body.size() != std::stoi(response.headers.find("Content-Length")->second))
{
char buffer[8192];
int recieved = recv(sockfd, buffer, 8191, 0);
buffer[recieved + 1] = '\0';
response.parse(std::string(buffer));
}

std::cout << response.getHeaderString() << std::endl;
}
};
Binary file added test
Binary file not shown.
8 changes: 8 additions & 0 deletions test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include "client.hpp"
#include <iostream>

int main()
{
HTTPClient::getFile("http://neverssl.com/");
return 0;
}

0 comments on commit 22cacf7

Please sign in to comment.