/* This file is part of libmicrospdy Copyright Copyright (C) 2013 Andrey Uzunov This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ /** * @file fileserver.c * @brief Simple example how the lib can be used for serving * files directly read from the system * @author Andrey Uzunov */ //for asprintf #define _GNU_SOURCE #include <unistd.h> #include <stdlib.h> #include <stdint.h> #include <stdbool.h> #include <string.h> #include <stdio.h> #include <ctype.h> #include <errno.h> #include "microspdy.h" #include "time.h" int run = 1; char* basedir; #define GET_MIME_TYPE(fname, mime) do {\ unsigned int __len = strlen(fname);\ if (__len < 4 || '.' != (fname)[__len - 4]) \ { \ (mime) = strdup("application/octet-stream");\ printf("MIME for %s is applic...\n", (fname));\ }\ else {\ const char * __ext = &(fname)[__len - 3];\ if(0 == strcmp(__ext, "jpg")) (mime) = strdup("image/jpeg");\ else if(0 == strcmp(__ext, "png")) (mime) = strdup("image/png");\ else if(0 == strcmp(__ext, "css")) (mime) = strdup("text/css");\ else if(0 == strcmp(__ext, "gif")) (mime) = strdup("image/gif");\ else if(0 == strcmp(__ext, "htm")) (mime) = strdup("text/html");\ else \ { \ (mime) = strdup("application/octet-stream");\ printf("MIME for %s is applic...\n", (fname));\ }\ }\ if(NULL == (mime))\ {\ printf("no memory\n");\ abort();\ }\ } while (0) static const char *DAY_NAMES[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static const char *MONTH_NAMES[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; //taken from http://stackoverflow.com/questions/2726975/how-can-i-generate-an-rfc1123-date-string-from-c-code-win32 //and modified for linux char *Rfc1123_DateTimeNow() { const int RFC1123_TIME_LEN = 29; time_t t; struct tm tm; char * buf = malloc(RFC1123_TIME_LEN+1); if (NULL == buf) return NULL; time(&t); gmtime_r( &t, &tm); strftime(buf, RFC1123_TIME_LEN+1, "---, %d --- %Y %H:%M:%S GMT", &tm); memcpy(buf, DAY_NAMES[tm.tm_wday], 3); memcpy(buf+8, MONTH_NAMES[tm.tm_mon], 3); return buf; } ssize_t response_callback (void *cls, void *buffer, size_t max, bool *more) { FILE *fd =(FILE*)cls; int ret = fread(buffer,1,max,fd); *more = feof(fd) == 0; //if(!(*more)) // fclose(fd); return ret; } void response_done_callback(void *cls, struct SPDY_Response *response, struct SPDY_Request *request, enum SPDY_RESPONSE_RESULT status, bool streamopened) { (void)streamopened; (void)status; //printf("answer for %s was sent\n", (char *)cls); /*if(SPDY_RESPONSE_RESULT_SUCCESS != status) { printf("answer for %s was NOT sent, %i\n", (char *)cls,status); }*/ SPDY_destroy_request(request); SPDY_destroy_response(response); if(NULL!=cls)fclose(cls); } void standard_request_handler(void *cls, struct SPDY_Request * request, uint8_t priority, const char *method, const char *path, const char *version, const char *host, const char *scheme, struct SPDY_NameValue * headers, bool more) { (void)cls; (void)request; (void)priority; (void)host; (void)scheme; (void)headers; (void)method; (void)version; (void)more; struct SPDY_Response *response=NULL; struct SPDY_NameValue *resp_headers; char *fname; char *fsize; char *mime=NULL; char *date=NULL; ssize_t filesize = -666; FILE *fd = NULL; int ret = -666; //printf("received request for '%s %s %s'\n", method, path, version); if(strlen(path) > 1 && NULL == strstr(path, "..") && '/' == path[0]) { asprintf(&fname,"%s%s",basedir,path); if(0 == access(fname, R_OK)) { if(NULL == (fd = fopen(fname,"r")) || 0 != (ret = fseek(fd, 0L, SEEK_END)) || -1 == (filesize = ftell(fd)) || 0 != (ret = fseek(fd, 0L, SEEK_SET))) { printf("Error on opening %s\n%p %i %zd\n",fname, fd, ret, filesize); response = SPDY_build_response(SPDY_HTTP_INTERNAL_SERVER_ERROR,NULL,SPDY_HTTP_VERSION_1_1,NULL,NULL,0); } else { if(NULL == (resp_headers = SPDY_name_value_create())) { printf("SPDY_name_value_create failed\n"); abort(); } date = Rfc1123_DateTimeNow(); if(NULL == date || SPDY_YES != SPDY_name_value_add(resp_headers,SPDY_HTTP_HEADER_DATE,date)) { printf("SPDY_name_value_add or Rfc1123_DateTimeNow failed\n"); abort(); } free(date); if(-1 == asprintf(&fsize, "%zd", filesize) || SPDY_YES != SPDY_name_value_add(resp_headers,SPDY_HTTP_HEADER_CONTENT_LENGTH,fsize)) { printf("SPDY_name_value_add or asprintf failed\n"); abort(); } free(fsize); GET_MIME_TYPE(path,mime); if(SPDY_YES != SPDY_name_value_add(resp_headers,SPDY_HTTP_HEADER_CONTENT_TYPE,mime)) { printf("SPDY_name_value_add failed\n"); abort(); } free(mime); if(SPDY_YES != SPDY_name_value_add(resp_headers,SPDY_HTTP_HEADER_SERVER,"libmicrospdy/fileserver")) { printf("SPDY_name_value_add failed\n"); abort(); } response = SPDY_build_response_with_callback(200,NULL, SPDY_HTTP_VERSION_1_1,resp_headers,&response_callback,fd,SPDY_MAX_SUPPORTED_FRAME_SIZE); SPDY_name_value_destroy(resp_headers); } if(NULL==response){ printf("no response obj\n"); abort(); } if(SPDY_queue_response(request,response,true,false,&response_done_callback,fd)!=SPDY_YES) { printf("queue\n"); abort(); } free(fname); return; } free(fname); } if(strcmp(path,"/close")==0) { run = 0; } response = SPDY_build_response(SPDY_HTTP_NOT_FOUND,NULL,SPDY_HTTP_VERSION_1_1,NULL,NULL,0); printf("Not found %s\n",path); if(NULL==response){ printf("no response obj\n"); abort(); } if(SPDY_queue_response(request,response,true,false,&response_done_callback,NULL)!=SPDY_YES) { printf("queue\n"); abort(); } } int main (int argc, char *const *argv) { unsigned long long timeoutlong=0; struct timeval timeout; int ret; fd_set read_fd_set; fd_set write_fd_set; fd_set except_fd_set; int maxfd = -1; struct SPDY_Daemon *daemon; if(argc != 5) { printf("Usage: %s cert-file key-file base-dir port\n", argv[0]); return 1; } SPDY_init(); daemon = SPDY_start_daemon(atoi(argv[4]), argv[1], argv[2], NULL, NULL, &standard_request_handler, NULL, NULL, SPDY_DAEMON_OPTION_SESSION_TIMEOUT, 1800, SPDY_DAEMON_OPTION_END); if(NULL==daemon){ printf("no daemon\n"); return 1; } basedir = argv[3]; do { FD_ZERO(&read_fd_set); FD_ZERO(&write_fd_set); FD_ZERO(&except_fd_set); ret = SPDY_get_timeout(daemon, &timeoutlong); if(SPDY_NO == ret || timeoutlong > 1000) { timeout.tv_sec = 1; timeout.tv_usec = 0; } else { timeout.tv_sec = timeoutlong / 1000; timeout.tv_usec = (timeoutlong % 1000) * 1000; } maxfd = SPDY_get_fdset (daemon, &read_fd_set, &write_fd_set, &except_fd_set); ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout); switch(ret) { case -1: printf("select error: %i\n", errno); break; case 0: break; default: SPDY_run(daemon); break; } } while(run); SPDY_stop_daemon(daemon); SPDY_deinit(); return 0; }