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