1 // HttpDaemon.cc
2 // This file is part of libpbe; see http://anyterm.org/
3 // (C) 2005 Philip Endecott
4 
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 
19 
20 #include "HttpDaemon.hh"
21 
22 #include "HttpRequest.hh"
23 #include "parse_http_request.hh"
24 #include "HttpResponse.hh"
25 #include "rfcdate.hh"
26 
27 #include <iostream>
28 #include <sstream>
29 
30 using namespace std;
31 
32 
33 namespace pbe {
34 
35 
session(FileDescriptor & in_fd,FileDescriptor & out_fd)36 void HttpDaemon::session(FileDescriptor& in_fd, FileDescriptor& out_fd)
37 {
38   session_start();
39 
40 #ifndef LIBPBE_HAS_FILEDESCRIPTOR_STREAMS
41 #error "Sorry, there is a problem with your compiler / C++ library.  Please ask for assistance"
42 #endif
43   FileDescriptor::istream in_strm(in_fd);
44 
45   bool close_connection=!keepalive;
46   do {
47     HttpRequest req;
48     HttpResponse resp;
49     try {
50       try {
51 	req = parse_http_request(in_strm);
52 	// Should look at Host: header and consider complete URIs in
53 	// request line
54 	if (req.http_version!="HTTP/1.1") {
55 	  // We should send a 1.0 response if the request was for 1.0; i.e.
56           // we should send a content-length header and not use chunked encoding.
57 	  close_connection=true;
58 	}
59 	if (req.headers["Connection"]=="close") {
60 	  // could be other tokens in the line.  Should be case-insensitive.
61 	  close_connection=true;
62 	}
63 	if (req.headers.find("Host")==req.headers.end()) {
64 	  resp.status_code=400;
65 	  resp.reason_phrase="Bad Request (missing Host: header)";
66 
67 	} else if (req.method!="GET" && req.method!="POST") {
68 	  resp.status_code=405;
69 	  resp.reason_phrase="Method not allowed";
70 	  resp.headers["Allow"]="GET POST";
71 	  // should check for Expect: header and reject with 417 response.
72 
73 	} else {
74 	  handle(req,resp);
75 	}
76       }
77       catch (HttpRequestSyntaxError& E) {
78 	resp.status_code=400;
79 	resp.reason_phrase="Malformed request";
80 	close_connection=true;
81       }
82       catch (HttpAuthenticator::NotAuthenticated& NA) {
83 	resp.status_code=401;
84 	resp.reason_phrase="Unauthorised";
85 	resp.headers["WWW-Authenticate"]="Basic realm=\"Anyterm\"";
86       }
87       RETHROW_MISC_EXCEPTIONS;
88     }
89     catch (Exception& E) {
90       resp.status_code=500;
91       ostringstream s;
92       s << "Server error: ";
93       E.report(s);
94       resp.reason_phrase=s.str();
95     }
96     if (resp.status_code!=200) {
97       close_connection=true;
98       // Actually we don't need to do this, but maybe it is safer
99     }
100     if (close_connection) {
101       resp.headers["Connection"]="close";
102     }
103     resp.headers["Date"]=rfc_date();
104     resp.send(out_fd);
105   } while (!close_connection);
106 }
107 
108 
109 
authenticate(HttpRequest & req)110 void HttpDaemon::authenticate(HttpRequest& req)
111 {
112   if (authenticator) {
113     HttpRequest::headers_t::const_iterator i = req.headers.find("Authorization");
114     if (i==req.headers.end()) {
115       throw HttpAuthenticator::NotAuthenticated();
116     }
117     string credentials = i->second;
118     req.userinfo = (*authenticator)(credentials);
119   }
120 }
121 
122 
123 };
124 
125