Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Simple HTTP Server  (Read 7238 times)

0 Members and 1 Guest are viewing this topic.

kkitsune

  • Newbie
  • *
  • Posts: 6
    • View Profile
Simple HTTP Server
« on: August 27, 2009, 08:51:39 pm »
I am trying to make a simple (like in really simple) HTTP server only for testing purposes. But it won't send back data to client (the client is using sf::Http).
I based my work on this simple application but with stripped authentication because SFML doesn't support it (not a problem). Why rewrite it? Because I want to use the WONDERFUL SFML in place of raw WinSocks. If someone has any help for me I will be very gratefull!
Thanks in advance!

Here is all the code of the server :
Code: [Select]
#include <boost/lexical_cast.hpp>
#include <SFML/Network.hpp>
#include <iostream>
#include <sstream>
#include <string>
#include <ctime>
#include <map>

using namespace sf;
using namespace std;
using namespace boost;

struct http_request
{
http_request() : authentication_given(false) {}

string method;
string path;
map<string, string> params;

string accept;
string accept_language;
string accept_encoding;
string user_agent;

/* status: used to transmit server's error status, such as
o  202 OK
o  404 Not Found
and so on */
string status;

/* auth_realm: allows to set the basic realm for an authentication,
no need to additionally set status if set */
string auth_realm;

string answer;

/*   authentication_given is true when the user has entered a username and password.
  These can then be read from username and password */
bool authentication_given;
string username;
string password;
};

void SplitGetReq(string get_req, string& path, map<string, string>& params)
{
// Remove trailing newlines
if(get_req[get_req.size() - 1] == '\x0d' || get_req[get_req.size() - 1] == '\x0a')
get_req = get_req.substr(0, get_req.size() - 1);

if(get_req[get_req.size() - 1] == '\x0d' || get_req[get_req.size() - 1] == '\x0a')
get_req=get_req.substr(0, get_req.size() - 1);

// Remove potential Trailing HTTP/1.x
if(get_req.size() > 7)
if (get_req.substr(get_req.size()-8, 7) == "HTTP/1.")
get_req = get_req.substr(0, get_req.size() - 9);

string::size_type qm = get_req.find("?");
if(qm != string::npos)
{
string url_params = get_req.substr(qm + 1);

path = get_req.substr(0, qm);

// Appending a '&' so that there are as many '&' as name-value pairs.
// It makes it easier to split the url for name value pairs, he he he
url_params += "&";

string::size_type next_amp = url_params.find("&");

while(next_amp != std::string::npos)
{
string name_value = url_params.substr(0, next_amp);
url_params        = url_params.substr(next_amp+1);
next_amp          = url_params.find("&");

string::size_type posocketequal = name_value.find("=");

string nam = name_value.substr(0, posocketequal);
string val = name_value.substr(posocketequal + 1);

std::string::size_type posocketplus;
while((posocketplus = val.find("+")) != string::npos)
val.replace(posocketplus, 1, " ");

// Replacing %xy notation
string::size_type posockethex = 0;
while((posockethex = val.find("%", posockethex)) != string::npos)
{
stringstream h;
h << val.substr(posockethex + 1, 2);
h << hex;

int i;
h >> i;

stringstream f;
f << static_cast<char>(i);
std::string s;
f >> s;

val.replace(posockethex, 3, s);
posockethex++;
}

params.insert(map<string, string>::value_type(nam, val));
}
}
else
path = get_req;
}

void Process(http_request* r)
{
string title;
string body;

if(r->path == "/")
r->answer = "This is a Message from a server!";
else if(r->path == "/test")
r->answer = (r->params["num"] != "") ? "This is a number : " + r->params["num"] + "!" : "There is no number!";
else
{
r->status = "404 Not Found";
r->answer = "<html><head><title>Wrong URL</title></head><body><h1>Wrong URL</h1>Path is : &gt;" + r->path + "&lt;</body></html>";
}
}

int main()
{
SocketTCP Listener;
if(!Listener.Listen(8080))
{
cout << "Unable to listen to the port 8080" << endl;
system("pause");
return 0;
}

SelectorTCP Selector;
Selector.Add(Listener);
while(true)
{
unsigned int NbSockets = Selector.Wait();
for(unsigned int i = 0; i < NbSockets; ++i)
{
SocketTCP Socket = Selector.GetSocketReady(i);

if(Socket == Listener)
{
IPAddress Address;
SocketTCP Client;
Listener.Accept(Client, &Address);
cout << "Client connected ! (" << Address << ")" << endl;

Selector.Add(Client);
}
else
{
if(!Socket.IsValid())
{
Socket.Close();
Selector.Remove(Socket);
continue;
}

http_request req;

string ReceivedStr;
size_t Size = 0;
char Buffer[1024];
while(Socket.Receive(Buffer, sizeof(Buffer), Size) == Socket::Done)
ReceivedStr.append(Buffer, Buffer + Size);

if(ReceivedStr.empty())
{
Socket.Close();
Selector.Remove(Socket);
continue;
}

if(ReceivedStr.find("GET") == 0)
req.method = "GET";
else if(ReceivedStr.find("POST") == 0)
req.method = "POST";

string path;
map<string, string> params;

size_t posStartPath = ReceivedStr.find_first_not_of(" ", 3);

SplitGetReq(ReceivedStr.substr(posStartPath), path, params);

req.status = "202 OK";
req.path   = path;
req.params = params;

Process(&req);

time_t ltime;
time(&ltime);
tm* gmt= gmtime(&ltime);
char* asctime_remove_nl = asctime(gmt);
asctime_remove_nl[24] = 0;

string Response = "HTTP/1.1 ";
Response += req.status;
Response += "\r\n";
Response += "Date: ";
Response += asctime_remove_nl;
Response += "\r\n";
Response += "Server: NumsgilTestServer (Windows)\r\n";
Response += "Connection: close\r\n";
Response += "Content-Type: text/html; charset=ISO-8859-1\r\n";
Response += "Content-Length: ";
Response += lexical_cast<string>(req.answer.size());
Response += "\r\n\r\n";
Response += req.answer;

if(Socket.Send(Response.c_str(), Response.size()) != Socket::Done)
cout << "Cannot send response to client" << endl;

Socket.Close();
Selector.Remove(Socket);
}
}
}

Selector.Clear();
Listener.Close();
return 0;
}

Jaenis

  • Newbie
  • *
  • Posts: 48
    • View Profile
Simple HTTP Server
« Reply #1 on: August 28, 2009, 06:30:45 am »
Socket::Receive() is a blocking function if there is no more data, so something like this will let you send reply back to client:
Code: [Select]
while(Socket.Receive(Buffer, sizeof(Buffer), Size) == Socket::Done)
{
ReceivedStr.append(Buffer, Buffer + Size);

// Break out if received less data than buffer can hold
if (Size < sizeof(Buffer))
break;
}

kkitsune

  • Newbie
  • *
  • Posts: 6
    • View Profile
Simple HTTP Server
« Reply #2 on: August 28, 2009, 05:13:44 pm »
Thanks a lot, It works now a lot better but now, my client is receiving strangely formated data...

This is the code of the client :
Code: [Select]
#include <boost/lexical_cast.hpp>
#include <SFML/Network.hpp>
#include <iostream>

using namespace sf;
using namespace std;
using namespace boost;

Http::Response SendRequest(const string& command)
{
string path = "/";
path += command;

Http::Request request(Http::Request::Get, path);

Http server("localhost", 8080);
return server.SendRequest(request);
}

int main()
{
int num = 0;
while(true)
{
cout << "Please enter a number : ";
cin >> num;
if(cin.fail())
{
fflush(stdin);
cin.clear();
cout << "Please enter a valid number!" << endl;
}
else break;
}

cout << SendRequest("test?num=" + lexical_cast<string>(num)).GetBody().c_str() << endl;

system("pause");
return 0;
}


And this is its output
Code: [Select]
Please enter a number : 123
This is a number : 123 HTTP/1.0
content-length: 0
from: user@sfml-dev.org
host: localhost
user-agent: libsfml-network/1.x
!

Jaenis

  • Newbie
  • *
  • Posts: 48
    • View Profile
Simple HTTP Server
« Reply #3 on: August 29, 2009, 05:13:23 am »
Quote from: "kkitsune"
Thanks a lot, It works now a lot better but now, my client is receiving strangely formated data...

I tried your code with firefox yesterday.
Your SplitGetReq is expecting only the first line of the data, but you were providing all data that browser sent to you.

So, just splitting that ReceivedStr from newline will fix it
Code: [Select]
size_t posStartPath = ReceivedStr.find_first_not_of(" ", 3);
size_t posEndPath = ReceivedStr.find_first_of("\n\r", 3);
SplitGetReq(ReceivedStr.substr(posStartPath,posEndPath-posStartPath), path, params);

kkitsune

  • Newbie
  • *
  • Posts: 6
    • View Profile
Simple HTTP Server
« Reply #4 on: August 29, 2009, 04:40:25 pm »
Thanks a million!

 

anything