-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 22cacf7
Showing
4 changed files
with
313 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |