| /* |
| 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; |
| } |
| |