|
| 1 | +/** |
| 2 | + httpserver |
| 3 | + ResourceHost.cpp |
| 4 | + Copyright 2011-2012 Ramsey Kant |
| 5 | +
|
| 6 | + Licensed under the Apache License, Version 2.0 (the "License"); |
| 7 | + you may not use this file except in compliance with the License. |
| 8 | + You may obtain a copy of the License at |
| 9 | +
|
| 10 | + http://www.apache.org/licenses/LICENSE-2.0 |
| 11 | +
|
| 12 | + Unless required by applicable law or agreed to in writing, software |
| 13 | + distributed under the License is distributed on an "AS IS" BASIS, |
| 14 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 15 | + See the License for the specific language governing permissions and |
| 16 | + limitations under the License. |
| 17 | +*/ |
| 18 | + |
| 19 | +#include "ResourceHost.h" |
| 20 | + |
| 21 | +ResourceHost::ResourceHost(std::string base) { |
| 22 | + cacheMap = NULL; |
| 23 | + diskBasePath = base; |
| 24 | + |
| 25 | + // Check to see if the disk base path is a valid path |
| 26 | + |
| 27 | + |
| 28 | + // Initialize cache map..possibly preload cache with files as a future feature? |
| 29 | + cacheMap = new std::map<std::string, Resource*>(); |
| 30 | +} |
| 31 | + |
| 32 | +ResourceHost::~ResourceHost() { |
| 33 | + clearCache(); |
| 34 | + |
| 35 | + // Delete the map instance |
| 36 | + delete cacheMap; |
| 37 | +} |
| 38 | + |
| 39 | +/** |
| 40 | + * Load File |
| 41 | + * Read a file from disk and load it into the memory cache |
| 42 | + * |
| 43 | + * @param path Full disk path of the file |
| 44 | + * @param sb Filled in stat struct |
| 45 | + * @return Return's the resource object upon successful load |
| 46 | + */ |
| 47 | +Resource* ResourceHost::loadFile(std::string path, struct stat sb) { |
| 48 | + std::ifstream file; |
| 49 | + unsigned int len = 0; |
| 50 | + |
| 51 | + // Open the file |
| 52 | + file.open(path.c_str(), std::ios::binary); |
| 53 | + |
| 54 | + // Return null if failed |
| 55 | + if(!file.is_open()) |
| 56 | + return NULL; |
| 57 | + |
| 58 | + // Get the length of the file |
| 59 | + /*file.seekg(0, std::ios::end); |
| 60 | + len = file.tellg(); |
| 61 | + file.seekg(0, std::ios::beg);*/ |
| 62 | + len = sb.st_size; |
| 63 | + |
| 64 | + // Allocate memory for contents of file and read in the contents |
| 65 | + byte* fdata = new byte[len]; |
| 66 | + file.read((char*)fdata, len); |
| 67 | + |
| 68 | + // Close the file |
| 69 | + file.close(); |
| 70 | + |
| 71 | + // Create a new Resource object and setup it's contents |
| 72 | + Resource* res = new Resource(path); |
| 73 | + res->setData(fdata, len); |
| 74 | + |
| 75 | + // Insert the resource into the map |
| 76 | + cacheMap->insert(std::pair<std::string, Resource*>(res->getLocation(), res)); |
| 77 | + |
| 78 | + return res; |
| 79 | +} |
| 80 | + |
| 81 | +/** |
| 82 | + * Dump and delete all resources in the cache, then clear it out |
| 83 | + */ |
| 84 | +void ResourceHost::clearCache() { |
| 85 | + // Cleanup all Resource objects |
| 86 | + std::map<std::string, Resource*>::const_iterator it; |
| 87 | + for(it = cacheMap->begin(); it != cacheMap->end(); ++it) { |
| 88 | + delete it->second; |
| 89 | + } |
| 90 | + cacheMap->clear(); |
| 91 | +} |
| 92 | + |
| 93 | +/** |
| 94 | + * Return an HTML directory listing provided by the relative path dirPath |
| 95 | + * |
| 96 | + * @param path Full disk path of the file |
| 97 | + * @param uri Relative webserver URI |
| 98 | + * @return String representation of the directory. Blank string if invalid directory |
| 99 | + */ |
| 100 | +std::string ResourceHost::listDirectory(std::string path, std::string uri) { |
| 101 | + std::stringstream ret; |
| 102 | + ret << "<html><head><title>" << uri << "</title></head><body>"; |
| 103 | + |
| 104 | + DIR *dir; |
| 105 | + struct dirent *ent; |
| 106 | + |
| 107 | + dir = opendir(path.c_str()); |
| 108 | + if(dir == NULL) |
| 109 | + return ""; |
| 110 | + |
| 111 | + // Page title, displaying the URI of the directory being listed |
| 112 | + ret << "<h1>Index of " << uri << "</h1><hr><br />"; |
| 113 | + |
| 114 | + // Add all files and directories to the return |
| 115 | + while((ent = readdir(dir)) != NULL) { |
| 116 | + // Skip any 'hidden' files (starting with a '.') |
| 117 | + if(ent->d_name[0] == '.') |
| 118 | + continue; |
| 119 | + |
| 120 | + // Display link to object in directory: |
| 121 | + ret << "<a href=\"" << uri << ent->d_name << "\">" << ent->d_name << "</a><br />"; |
| 122 | + } |
| 123 | + |
| 124 | + // Close the directory |
| 125 | + closedir(dir); |
| 126 | + |
| 127 | + ret << "</body></html>"; |
| 128 | + |
| 129 | + return ret.str(); |
| 130 | +} |
| 131 | + |
| 132 | +/** |
| 133 | + * Retrieve a resource from the File system |
| 134 | + * The memory cache will be checked before going out to disk |
| 135 | + * |
| 136 | + * @param uri The URI sent in the request |
| 137 | + * @return NULL if unable to load the resource. Resource object |
| 138 | + */ |
| 139 | +Resource* ResourceHost::getResource(std::string uri) { |
| 140 | + if(uri.length() > 255 || uri.empty()) |
| 141 | + return NULL; |
| 142 | + |
| 143 | + std::string path = diskBasePath + uri; |
| 144 | + Resource* res = NULL; |
| 145 | + |
| 146 | + // Check the cache first: |
| 147 | + std::map<std::string, Resource*>::const_iterator it; |
| 148 | + it = cacheMap->find(path); |
| 149 | + // If it isn't the element past the end (end()), then a resource was found |
| 150 | + if(it != cacheMap->end()) { |
| 151 | + res = it->second; |
| 152 | + return res; |
| 153 | + } |
| 154 | + |
| 155 | + // Not in cache, check the disk |
| 156 | + |
| 157 | + // Gather info about the resource with stat: determine if it's a directory or file, check if its owned by group/user, modify times |
| 158 | + struct stat sb; |
| 159 | + if(stat(path.c_str(), &sb) == -1) |
| 160 | + return NULL; // File not found |
| 161 | + |
| 162 | + // Make sure the webserver USER owns the files |
| 163 | + if(!(sb.st_mode & S_IRWXU)) |
| 164 | + return NULL; |
| 165 | + |
| 166 | + // Determine file type |
| 167 | + if(sb.st_mode & S_IFDIR) { // Directory |
| 168 | + // Generate an HTML directory listing |
| 169 | + std::string listing = listDirectory(path, uri); |
| 170 | + unsigned int slen = listing.length(); |
| 171 | + char* sdata = new char[slen]; |
| 172 | + strncpy(sdata, listing.c_str(), slen); |
| 173 | + |
| 174 | + res = new Resource(path, true); |
| 175 | + res->setData((byte*)sdata, slen); |
| 176 | + |
| 177 | + // Cache the listing |
| 178 | + cacheMap->insert(std::pair<std::string, Resource*>(res->getLocation(), res)); |
| 179 | + } else if(sb.st_mode & S_IFREG) { // Regular file |
| 180 | + // Attempt to load the file into memory from the FS |
| 181 | + res = loadFile(path, sb); |
| 182 | + } else { // Something else..device, socket, symlink |
| 183 | + return NULL; |
| 184 | + } |
| 185 | + |
| 186 | + return res; |
| 187 | +} |
0 commit comments