Download code

Jump to: navigation, search

Back to Web_server_(C_Plus_Plus)

Download for Windows: zip

Download for UNIX: zip, tar.gz, tar.bz2

http.cpp

  1 /* The authors of this work have released all rights to it and placed it
  2 in the public domain under the Creative Commons CC0 1.0 waiver
  3 (http://creativecommons.org/publicdomain/zero/1.0/).
  4 
  5 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  6 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  7 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  8 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  9 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 10 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 11 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 12 
 13 Retrieved from: http://en.literateprograms.org/Web_server_(C_Plus_Plus)?oldid=18666
 14 */
 15 
 16 #include"http.hpp"
 17 
 18 #include<sys/socket.h>
 19 #include<netinet/in.h>
 20 
 21 namespace lp {
 22 http::~http()
 23 {
 24 	if(m_cs>0) close(m_cs);
 25 	if(m_ss>0) close(m_ss);
 26 }
 27 bool http::dorecv()
 28 {
 29 	char b[1024];
 30 	int nrecv;
 31 	if((nrecv=recv(m_cs, b, 1023, 0))<0) throw http_recv_error();
 32 	if(!nrecv) return false;
 33 	b[nrecv]='\0';
 34 	m_rbuf+=b;
 35 	return true;
 36 }
 37 std::string http::getline()
 38 {
 39 	std::string line;
 40 	size_t ix;
 41 	while((ix=m_rbuf.find('\n'))==std::string::npos) {
 42 		if(!dorecv()) {
 43 			line=m_rbuf;
 44 			m_rbuf.erase();
 45 			return line;
 46 		}
 47 	}
 48 	line=m_rbuf.substr(0, ix);
 49 	m_rbuf.erase(0, ix+1);
 50 	if(!line.empty() && line[line.size()-1]=='\r') 
 51 		line.erase(line.size()-1);	// Remove trailing '\r'
 52 	return line;
 53 }
 54 void http::dosend(const std::string &s)
 55 {
 56 	if(send(m_cs, s.c_str(), s.size(), 0)!=(int)s.size()) throw http_send_error();
 57 }
 58 void http::setup()
 59 {
 60 	try {
 61 	struct sockaddr_in addr;
 62 	if((m_ss=socket(PF_INET, SOCK_STREAM, 0))==-1) throw http_socket_error();
 63 	memset((void*)&addr, 0, sizeof addr);
 64 	addr.sin_family=AF_INET;
 65 	addr.sin_port=htons(m_port);
 66 	addr.sin_addr.s_addr=INADDR_ANY;
 67 	if(bind(m_ss, (struct sockaddr*)&addr, sizeof addr)==-1) throw http_bind_error();
 68 	if(listen(m_ss, 1)==-1) throw http_listen_error();
 69 	} catch(...) {
 70 		if(m_ss>0) close(m_ss);
 71 		m_ss=0;
 72 		throw;
 73 	}
 74 }
 75 bool http::wait(int timeout)
 76 {
 77 	if(!m_ss) setup();
 78 	struct timeval tmout;
 79 	struct timeval *ptmout;
 80 	if(timeout) {
 81 		tmout.tv_sec=timeout/1000;
 82 		tmout.tv_usec=(timeout%1000)*1000;
 83 		ptmout=&tmout;
 84 	} else ptmout=NULL;
 85 	fd_set rset;
 86 	FD_ZERO(&rset);
 87 	FD_SET(m_ss, &rset);
 88 	if(select(m_ss+1, &rset, 0, 0, ptmout)>0) {
 89 		struct sockaddr_in client_addr;
 90 		socklen_t addrsize(sizeof client_addr);
 91 
 92 		if((m_cs=accept(m_ss, (struct sockaddr*)&client_addr, &addrsize))==-1)
 93 			throw http_accept_error();
 94 	
 95 		return true;
 96 	} else return false;
 97 }
 98 
 99 std::string formvalue(const std::string &raw)
100 {
101 	std::string ret;
102 	for(size_t ix=0; ix<raw.size(); ++ix) {
103 		if(raw[ix]=='+') ret+=' ';
104 		else if(raw[ix]=='%') {
105 			int val(0);
106 			if(++ix<raw.size()) 
107 				val=16*(isalpha(raw[ix]) ? toupper(raw[ix])+10-'A' : raw[ix]-'0');
108 			if(++ix<raw.size()) 
109 				val+=isalpha(raw[ix]) ? toupper(raw[ix])+10-'A' : raw[ix]-'0';
110 			ret+=(char)val;
111 		} else ret+=raw[ix];
112 	}
113 	return ret;
114 }
115 
116 void readformdata(const std::string &str, std::map<std::string, std::string> &formvalues)
117 {
118 	std::string name, value;
119 	bool reading_name(true);
120 	for(size_t ix=0; ix<str.size(); ++ix) {
121 		if(str[ix]=='=') {
122 			reading_name=false;
123 		} else if(str[ix]=='&') {
124 			formvalues[name]=formvalue(value);
125 			name.clear();
126 			value.clear();
127 			reading_name=true;
128 		} else if(reading_name) {
129 			name+=str[ix];
130 		} else {
131 			value+=str[ix];
132 		}
133 	}
134 	if(!name.empty()) formvalues[name]=formvalue(value);
135 }
136 
137 const http_request &http::request()
138 {
139 	size_t ix;
140 
141 	m_request.error.clear();
142 	m_request.method.clear();
143 	m_request.path.clear();
144 	m_request.version.clear();
145 	m_request.fields.clear();
146 	m_request.formvalues.clear();
147 
148 	std::string line;
149 	if((line=getline()).empty()) m_request.error="Empty line";
150 
151 	// Method
152 	if((ix=line.find_first_of(" \t"))!=std::string::npos) {
153 		m_request.method=line.substr(0, ix);
154 		line.erase(0, ix);
155 		while(!line.empty() && isspace(line[0])) line.erase(0, 1);
156 
157 		// Path
158 		if((ix=line.find_first_of(" \t"))!=std::string::npos) {
159 			m_request.path=line.substr(0, ix);
160 			line.erase(0, ix);
161 			while(!line.empty() && isspace(line[0])) line.erase(0, 1);
162 
163 			// Form data in URL
164 			if((ix=m_request.path.find('?'))!=std::string::npos) {
165 				std::string formdata(m_request.path.substr(ix+1));
166 				m_request.path=m_request.path.substr(0, ix);
167 
168 				readformdata(formdata, m_request.formvalues);
169 			}
170 
171 			// Version
172 			m_request.version=line;
173 		} else m_request.path=line;
174 	} else m_request.error="Syntax error";
175 	if(m_request.version.empty()) return m_request;	// Old style HTTP request
176 
177 	// Fields
178 	while(!(line=getline()).empty()) {
179 		if((ix=line.find(':'))==std::string::npos) {
180 			m_request.error="Syntax error";
181 			return m_request;
182 		}
183 		std::string &value=m_request.fields[line.substr(0, ix)]=line.substr(ix+1);
184 		while(!value.empty() && isspace(value[0])) value.erase(0, 1);
185 	}
186 
187 	return m_request;
188 }
189 void http::reply(const std::string &msg, const std::string &ctype)
190 {
191 	std::string header("HTTP/1.1 200 Ok\r\nContent-Type: "+ctype+"\r\n\r\n");
192 	dosend(header+msg);
193 	close(m_cs);
194 	m_cs=0;
195 }
196 void http::error(const std::string &code, const std::string &msg)
197 {
198 	std::string header("HTTP/1.1 "+code+" "+msg+"\r\nContent-type: text/plain\r\n\r\n");
199 	std::string data(msg+"\r\n");
200 	dosend(header+data);
201 	close(m_cs);
202 	m_cs=0;
203 }
204 } // namespace lp


hijacker
hijacker
hijacker
hijacker

build.log

1 /tmp/litprog2756891/http.cpp: In member function 'void lp::http::setup()':
2 /tmp/litprog2756891/http.cpp:63:37: error: 'memset' was not declared in this scope
3 /tmp/litprog2756891/main.cpp: In function 'int main(int, char**)':
4 /tmp/litprog2756891/main.cpp:73:30: error: 'atoi' was not declared in this scope


http.hpp

 1 /* The authors of this work have released all rights to it and placed it
 2 in the public domain under the Creative Commons CC0 1.0 waiver
 3 (http://creativecommons.org/publicdomain/zero/1.0/).
 4 
 5 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 6 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 7 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 8 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 9 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
10 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
11 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12 
13 Retrieved from: http://en.literateprograms.org/Web_server_(C_Plus_Plus)?oldid=18666
14 */
15 
16 #ifndef HTTP_HPP_INCLUDE_GUARD
17 #define HTTP_HPP_INCLUDE_GUARD
18 
19 #include<string>
20 #include<map>
21 
22 namespace lp {
23 struct http_error {
24 	virtual const char *what()=0;
25 	virtual ~http_error() {}
26 };
27 struct http_socket_error: http_error {
28 	const char *what() {return "socket()";}
29 };
30 struct http_bind_error: http_error {
31 	const char *what() {return "bind()";}
32 };
33 struct http_listen_error: http_error {
34 	const char *what() {return "listen()";}
35 };
36 struct http_accept_error: http_error {
37 	const char *what() {return "accept()";}
38 };
39 struct http_recv_error: http_error {
40 	const char *what() {return "recv()";}
41 };
42 struct http_send_error: http_error {
43 	const char *what() {return "send()";}
44 };
45 struct http_request {
46 	std::string error;
47 	std::string method;
48 	std::string path;
49 	std::string version;
50 	std::map<std::string, std::string> fields;
51 	std::map<std::string, std::string> formvalues;
52 };
53 class http {
54 	int m_port;
55 	std::string m_path;
56 	int m_cs;		// Client socket
57 	int m_ss;		// Server socket
58 	std::string m_rbuf;
59 	http_request m_request;
60 
61 	void setup();
62 	bool dorecv();
63 	std::string getline();
64 	void dosend(const std::string &);
65 public:
66 	http(int port): m_port(port), m_cs(0), m_ss(0) {}
67 	~http();
68 	bool wait(int timeout=0);
69 	const http_request &request();
70 	void reply(const std::string &data, const std::string &ctype);
71 	void error(const std::string &code, const std::string &msg);
72 };
73 } // namespace lp
74 #endif


hijacker
hijacker
hijacker
hijacker

Makefile

# The authors of this work have released all rights to it and placed it
# in the public domain under the Creative Commons CC0 1.0 waiver
# (http://creativecommons.org/publicdomain/zero/1.0/).
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# 
# Retrieved from: http://en.literateprograms.org/Web_server_(C_Plus_Plus)?oldid=18666

all: lphttpd

lphttpd: main.o http.o
	c++ -o lphttpd -Wall main.o http.o

main.o: main.cpp http.hpp
	c++ -o main.o -Wall -c main.cpp

http.o: http.cpp http.hpp
	c++ -o http.o -Wall -c http.cpp


hijacker
hijacker
hijacker
hijacker

main.cpp

  1 /* The authors of this work have released all rights to it and placed it
  2 in the public domain under the Creative Commons CC0 1.0 waiver
  3 (http://creativecommons.org/publicdomain/zero/1.0/).
  4 
  5 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  6 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  7 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  8 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  9 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 10 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 11 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 12 
 13 Retrieved from: http://en.literateprograms.org/Web_server_(C_Plus_Plus)?oldid=18666
 14 */
 15 
 16 
 17 #include"http.hpp"
 18 
 19 #include<iostream>
 20 #include<string>
 21 #include<vector>
 22 
 23 std::string frameset(
 24 "<frameset rows=\"50%, 50%\">\n\
 25 <frame src=\"top\" /> <frame src=\"bottom\" /> </frameset>\n"
 26 );
 27 
 28 std::string root("<html><head><title>Chat</title></head>"+frameset+"</html>");
 29 
 30 struct chat_t {
 31 	std::vector<std::string> msgs;
 32 	std::string top(size_t howmany=10) {
 33 		std::string ret(
 34 "<html><head><meta http-equiv=\"refresh\" content=1 /></head>\n\
 35 <body><table border=1><colgroup span=2><col width=\"10%\" /><col width=\"90%\"></colgroup>\n\
 36 <tr><th>User id</th><th>Message</th></tr>\n"
 37 );
 38 
 39 		for(int n=(int)msgs.size()-howmany; n<(int)msgs.size(); ++n) {
 40 			ret+=" <tr>";
 41 			if(n>=0) ret+=msgs[n];
 42 			else ret+="<td> </td><td> </td>";
 43 			ret+="</tr>\n";
 44 		}
 45 
 46 		return ret+"</table></body></html>";
 47 	}
 48 
 49 	std::string bottom(std::map<std::string, std::string> formvalues) {
 50 		std::string userid(formvalues["userid"]);
 51 		if(userid.empty()) userid="anonymous";
 52 
 53 		std::string msg(formvalues["msg"]);
 54 		if(!msg.empty()) {
 55 			msgs.push_back("<td>"+userid+"</td><td>"+msg+"</td>\n");
 56 		}
 57 
 58 		return std::string(
 59 "<html><head></head><body>\n\
 60 <form action=bottom method=\"get\" value=\""+userid+"\">\n\
 61 <label for=userid>User id: </label><input type=text id=userid name=userid value=\""+userid+"\" />\n\
 62 <label for=msg>Text: </label><input type=text size=100 id=msg name=msg />\n\
 63 <input type=submit value=\"Send\" />\n\
 64 </form>\n\
 65 </body></html>\n"
 66 );
 67 	}
 68 };
 69 
 70 int main(int argc, char *argv[])
 71 {
 72 	try {
 73 	int port(argc>1?atoi(argv[1]):80);
 74 	chat_t chat;
 75 	
 76 	lp::http server(port);
 77 
 78 	while(server.wait()) {
 79 		const lp::http_request &request(server.request());
 80 		if(!request.error.empty()) std::cerr<<"ERROR: "<<request.error<<'\n';
 81 
 82 		if(request.method=="GET") {
 83 			if(request.path=="/") server.reply(root, "text/html");
 84 			else if(request.path.substr(0, 4)=="/top") 
 85 				server.reply(chat.top(), "text/html");
 86 			else if(request.path=="/bottom") 
 87 				server.reply(chat.bottom(request.formvalues), "text/html");
 88 			else server.error("404", "Not found "+request.path);
 89 				
 90 		} else {
 91 			server.error("501", "Not implemented");
 92 		}
 93 	}
 94 	} catch(lp::http_error &e) {
 95 		std::cerr<<"Exception: "<<e.what()<<'\n';
 96 	}
 97 
 98 	return 0;
 99 }
100 
101 


hijacker
hijacker
hijacker
hijacker