Implemented first bits of http_file filter which serves plain
authorAdam Dickmeiss <adam@indexdata.dk>
Thu, 19 Jan 2006 21:43:51 +0000 (21:43 +0000)
committerAdam Dickmeiss <adam@indexdata.dk>
Thu, 19 Jan 2006 21:43:51 +0000 (21:43 +0000)
files via HTTP. Needs to add XML config parsing + mime type reading.

src/Makefile.am
src/factory_static.cpp
src/filter_http_file.cpp [new file with mode: 0644]
src/filter_http_file.hpp [new file with mode: 0644]
src/util.cpp
src/util.hpp

index cedd45a..05bd100 100644 (file)
@@ -1,4 +1,4 @@
-## $Id: Makefile.am,v 1.45 2006-01-19 12:18:09 marc Exp $
+## $Id: Makefile.am,v 1.46 2006-01-19 21:43:51 adam Exp $
 
 MAINTAINERCLEANFILES = Makefile.in config.in config.hpp
 
@@ -28,6 +28,7 @@ libyp2_la_SOURCES = \
        filter_backend_test.cpp filter_backend_test.hpp \
        filter_session_shared.cpp filter_session_shared.hpp \
        filter_template.cpp filter_template.hpp \
+       filter_http_file.cpp filter_http_file.hpp \
        factory_static.cpp factory_static.hpp \
        pipe.cpp pipe.hpp \
        util.cpp util.hpp \
index 8e7f283..41bd521 100644 (file)
@@ -1,8 +1,7 @@
-/* $Id: factory_static.cpp,v 1.6 2006-01-15 20:03:14 adam Exp $
+/* $Id: factory_static.cpp,v 1.7 2006-01-19 21:43:51 adam Exp $
    Copyright (c) 2005, Index Data.
 
 %LICENSE%
-
 */
 
 #include <iostream>
@@ -19,6 +18,7 @@
 #include "filter_auth_simple.hpp"
 #include "filter_backend_test.hpp"
 #include "filter_frontend_net.hpp"
+#include "filter_http_file.hpp"
 #include "filter_log.hpp"
 #include "filter_multi.hpp"
 #include "filter_session_shared.hpp"
@@ -32,6 +32,7 @@ yp2::FactoryStatic::FactoryStatic()
         &yp2_filter_auth_simple,
         &yp2_filter_backend_test,
         &yp2_filter_frontend_net,        
+        &yp2_filter_http_file,
         &yp2_filter_log,
         &yp2_filter_multi,
         &yp2_filter_session_shared,
diff --git a/src/filter_http_file.cpp b/src/filter_http_file.cpp
new file mode 100644 (file)
index 0000000..533f575
--- /dev/null
@@ -0,0 +1,223 @@
+/* $Id: filter_http_file.cpp,v 1.1 2006-01-19 21:44:26 adam Exp $
+   Copyright (c) 2005, Index Data.
+
+%LICENSE%
+ */
+
+#include "config.hpp"
+
+#include "filter.hpp"
+#include "package.hpp"
+
+#include <boost/thread/mutex.hpp>
+
+#include "util.hpp"
+#include "filter_http_file.hpp"
+
+#include <list>
+#include <map>
+
+#include <yaz/zgdu.h>
+
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+namespace yf = yp2::filter;
+
+namespace yp2 {
+    namespace filter {
+        struct HttpFile::Area {
+            std::string m_url_path_prefix;
+            std::string m_file_root;
+        };
+        class HttpFile::Mime {
+            friend class Rep;
+            std::string m_type;
+        public:
+            Mime(std::string type);
+            Mime();
+        };
+        class HttpFile::Rep {
+            friend class HttpFile;
+
+            typedef std::list<Area> AreaList;
+            typedef std::map<std::string,Mime> MimeMap;
+
+            MimeMap m_ext_to_map;
+            AreaList m_area_list;
+            void fetch_uri(yp2::Session &session,
+                           Z_HTTP_Request *req, yp2::Package &package);
+            void fetch_file(yp2::Session &session,
+                            Z_HTTP_Request *req,
+                            std::string &fname, yp2::Package &package);
+            std::string get_mime_type(std::string &fname);
+        };
+    }
+}
+
+yf::HttpFile::Mime::Mime() {}
+
+yf::HttpFile::Mime::Mime(std::string type) : m_type(type) {}
+
+yf::HttpFile::HttpFile() : m_p(new Rep)
+{
+    m_p->m_ext_to_map["html"] = Mime("text/html");
+    m_p->m_ext_to_map["htm"] = Mime("text/html");
+    m_p->m_ext_to_map["png"] = Mime("image/png");
+    m_p->m_ext_to_map["txt"] = Mime("text/plain");
+    m_p->m_ext_to_map["text"] = Mime("text/plain");
+    m_p->m_ext_to_map["asc"] = Mime("text/plain");
+    m_p->m_ext_to_map["xml"] = Mime("application/xml");
+    m_p->m_ext_to_map["xsl"] = Mime("application/xml");
+
+    Area a;
+    a.m_url_path_prefix = "/etc";
+    a.m_file_root = "..";
+    m_p->m_area_list.push_back(a);
+}
+
+yf::HttpFile::~HttpFile()
+{
+}
+
+std::string yf::HttpFile::Rep::get_mime_type(std::string &fname)
+{
+    std::string file_part = fname;
+    std::string::size_type p = fname.find_last_of('/');
+    
+    if (p != std::string::npos)
+        file_part = fname.substr(p+1);
+
+    p = file_part.find_last_of('.');
+    std::string content_type;
+    if (p != std::string::npos)
+    {
+        std::string ext = file_part.substr(p+1);
+        MimeMap::const_iterator it = m_ext_to_map.find(ext);
+
+        if (it != m_ext_to_map.end())
+            content_type = it->second.m_type;
+    }
+    if (content_type.length() == 0)
+        content_type = "application/octet-stream";
+    return content_type;
+}
+
+void yf::HttpFile::Rep::fetch_file(yp2::Session &session,
+                                   Z_HTTP_Request *req,
+                                   std::string &fname, yp2::Package &package)
+{
+    struct stat sbuf;
+    yp2::odr o;
+    
+    if (stat(fname.c_str(), &sbuf))
+    {
+        Z_GDU *gdu = o.create_HTTP_Response(session, req, 404);
+        package.response() = gdu;
+        return;
+    }
+    if ((sbuf.st_mode & S_IFMT) != S_IFREG)
+    {
+        Z_GDU *gdu = o.create_HTTP_Response(session, req, 404);
+        package.response() = gdu;
+        return;
+    }
+    if (sbuf.st_size > (off_t) 1000000)
+    {
+        Z_GDU *gdu = o.create_HTTP_Response(session, req, 404);
+        package.response() = gdu;
+        return;
+    }
+    
+    FILE *f = fopen(fname.c_str(), "rb");
+    if (!f)
+    {
+        Z_GDU *gdu = o.create_HTTP_Response(session, req, 404);
+        package.response() = gdu;
+        return;
+    }
+    Z_GDU *gdu = o.create_HTTP_Response(session, req, 200);
+
+    Z_HTTP_Response *hres = gdu->u.HTTP_Response;
+    hres->content_len = sbuf.st_size;
+    hres->content_buf = (char*) odr_malloc(o, hres->content_len);
+    fread(hres->content_buf, 1, hres->content_len, f);
+
+    fclose(f);
+    
+    std::string content_type = get_mime_type(fname);
+
+    z_HTTP_header_add(o, &hres->headers,
+                      "Content-Type", content_type.c_str());
+    package.response() = gdu;
+}
+
+void yf::HttpFile::Rep::fetch_uri(yp2::Session &session,
+                                  Z_HTTP_Request *req, yp2::Package &package)
+{
+    bool sane = true;
+    std::string path = req->path;
+
+    // we don't consider ?, # yet..
+
+    // we don't allow ..
+    std::string::size_type p = path.find("..");
+    if (p != std::string::npos)
+        sane = false;
+
+    if (sane)
+    {
+        AreaList::const_iterator it;
+        for (it = m_area_list.begin(); it != m_area_list.end(); it++)
+        {
+            std::string::size_type l = it->m_url_path_prefix.length();
+
+            if (path.compare(0, l, it->m_url_path_prefix) == 0)
+            {
+                std::string fname = it->m_file_root + path;
+                fetch_file(session, req, fname, package);
+                return;
+            }
+        }
+    }
+    yp2::odr o;
+    Z_GDU *gdu = o.create_HTTP_Response(session, req, 404);
+    package.response() = gdu;
+}
+                         
+void yf::HttpFile::process(yp2::Package &package) const
+{
+    Z_GDU *gdu = package.request().get();
+    if (gdu && gdu->which == Z_GDU_HTTP_Request)
+        m_p->fetch_uri(package.session(), gdu->u.HTTP_Request, package);
+    else
+        package.move();
+}
+
+static yp2::filter::Base* filter_creator()
+{
+    return new yp2::filter::HttpFile;
+}
+
+extern "C" {
+    struct yp2_filter_struct yp2_filter_http_file = {
+        0,
+        "http_file",
+        filter_creator
+    };
+}
+
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * c-file-style: "stroustrup"
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
diff --git a/src/filter_http_file.hpp b/src/filter_http_file.hpp
new file mode 100644 (file)
index 0000000..b0ebdd4
--- /dev/null
@@ -0,0 +1,41 @@
+/* $Id: filter_http_file.hpp,v 1.1 2006-01-19 21:44:26 adam Exp $
+   Copyright (c) 2005, Index Data.
+
+%LICENSE%
+ */
+
+#ifndef FILTER_HTTP_FILE_HPP
+#define FILTER_HTTP_FILE_HPP
+
+#include <boost/scoped_ptr.hpp>
+
+#include "filter.hpp"
+
+namespace yp2 {
+    namespace filter {
+        class HttpFile : public Base {
+            class Rep;
+            class Area;
+            class Mime;
+            boost::scoped_ptr<Rep> m_p;
+        public:
+            HttpFile();
+            ~HttpFile();
+            void process(yp2::Package & package) const;
+        };
+    }
+}
+
+extern "C" {
+    extern struct yp2_filter_struct yp2_filter_http_file;
+}
+
+#endif
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * c-file-style: "stroustrup"
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
index d745065..c8cf573 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: util.cpp,v 1.11 2006-01-18 14:10:47 adam Exp $
+/* $Id: util.cpp,v 1.12 2006-01-19 21:43:51 adam Exp $
    Copyright (c) 2005, Index Data.
 
 %LICENSE%
@@ -318,6 +318,39 @@ Z_APDU *yp2::odr::create_scanResponse(Z_APDU *in_apdu,
     return apdu;
 }
 
+Z_GDU *yp2::odr::create_HTTP_Response(yp2::Session &session,
+                                      Z_HTTP_Request *hreq, int code)
+{
+    const char *response_version = "1.0";
+    bool keepalive = false;
+    if (!strcmp(hreq->version, "1.0")) 
+    {
+        const char *v = z_HTTP_header_lookup(hreq->headers, "Connection");
+        if (v && !strcmp(v, "Keep-Alive"))
+            keepalive = true;
+        else
+            session.close();
+        response_version = "1.0";
+    }
+    else
+    {
+        const char *v = z_HTTP_header_lookup(hreq->headers, "Connection");
+        if (v && !strcmp(v, "close"))
+            session.close();
+        else
+            keepalive = true;
+        response_version = "1.1";
+    }
+
+    Z_GDU *gdu = z_get_HTTP_Response(m_odr, code);
+    Z_HTTP_Response *hres = gdu->u.HTTP_Response;
+    hres->version = odr_strdup(m_odr, response_version);
+    if (keepalive)
+        z_HTTP_header_add(m_odr, &hres->headers, "Connection", "Keep-Alive");
+    
+    return gdu;
+}
+
 Z_ReferenceId **yp2::util::get_referenceId(Z_APDU *apdu)
 {
     switch (apdu->which)
index c905304..f610ce5 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: util.hpp,v 1.9 2006-01-18 10:57:27 adam Exp $
+/* $Id: util.hpp,v 1.10 2006-01-19 21:43:51 adam Exp $
    Copyright (c) 2005, Index Data.
 
 %LICENSE%
@@ -13,6 +13,8 @@
 
 #include <boost/utility.hpp>
 
+#include "package.hpp"
+
 namespace yp2 {
     namespace util  {
        bool pqf(ODR odr, Z_APDU *apdu, const std::string &q);
@@ -58,6 +60,8 @@ namespace yp2 {
         Z_APDU *create_scanResponse(Z_APDU *in_apdu,
                                     int error, const char *addinfo);
         Z_APDU *create_APDU(int type, Z_APDU *in_apdu);
+        Z_GDU *create_HTTP_Response(yp2::Session &session,
+                                    Z_HTTP_Request *req, int code);
     private:
         ODR m_odr;
     };