void t01_test_mime(){ INIT_LOCAL(); FAIL_IF_NOT_EQUAL_STR("text/plain", onion_mime_get("txt")); FAIL_IF_NOT_EQUAL_STR("text/plain", onion_mime_get("fdsfds")); FAIL_IF_NOT_EQUAL_STR("text/html", onion_mime_get("html")); FAIL_IF_NOT_EQUAL_STR("image/png", onion_mime_get("file.png")); FAIL_IF_NOT_EQUAL_STR("application/javascript", onion_mime_get("js")); onion_mime_set(NULL); END_LOCAL(); }
/** * @short Generates the necesary data to the output stream. */ void parse_file(const char *prefix, const char *filename, FILE * outfd, onion_assets_file * assets) { FILE *fd = fopen(filename, "r"); if (!fd) { fprintf(stderr, "ERROR: Cant open file %s: ", filename); perror(""); onion_assets_file_free(assets); exit(3); } char *fname = funcname(prefix, basename((char *)filename)); char buffer[4096]; buffer[4095] = 0; snprintf(buffer, sizeof(buffer) - 1, "onion_connection_status %s(void *_, onion_request *req, onion_response *res);", fname); onion_assets_file_update(assets, buffer); fprintf(stderr, "Parsing: %s to '%s'.\n", filename, buffer); fprintf(outfd, "onion_connection_status %s(void *_, onion_request *req, onion_response *res){\n static const char data[]={\n", fname); int r, i, l = 0; while ((r = fread(buffer, 1, sizeof(buffer) - 1, fd)) != 0) { for (i = 0; i < r; i++) { fprintf(outfd, "0x%02X, ", buffer[i] & 0x0FF); if ((i % 16) == 15) { fprintf(outfd, "\n"); } } l += r; } fprintf(outfd, "};\n"); const char *mime_type = onion_mime_get(filename); fprintf(outfd, " onion_response_set_length(res, %d);\n", l); fprintf(outfd, " onion_response_set_header(res, \"Content-Type\", \"%s\");\n", mime_type); fprintf(outfd, " return onion_response_write(res, data, sizeof(data));\n}\n\n"); fprintf(outfd, "const unsigned int %s_length = %d;\n\n", fname, l); fclose(fd); free(fname); }
/** * @short This shortcut returns the given file contents. * * It sets all the compilant headers (TODO), cache and so on. * * This is the recomended way to send static files; it even can use sendfile Linux call * if suitable (TODO). * * It does no security checks, so caller must be security aware. */ onion_connection_status onion_shortcut_response_file(const char *filename, onion_request *request, onion_response *res){ int fd=open(filename,O_RDONLY|O_CLOEXEC); if (fd<0) return OCS_NOT_PROCESSED; if(O_CLOEXEC == 0) { // Good compiler know how to cut this out int flags=fcntl(fd, F_GETFD); if (flags==-1){ ONION_ERROR("Retrieving flags from file descriptor"); } flags|=FD_CLOEXEC; if (fcntl(fd, F_SETFD, flags)==-1){ ONION_ERROR("Setting O_CLOEXEC to file descriptor"); } } struct stat st; if (stat(filename, &st)!=0){ ONION_WARNING("File does not exist: %s",filename); close(fd); return OCS_NOT_PROCESSED; } if (S_ISDIR(st.st_mode)){ close(fd); return OCS_NOT_PROCESSED; } size_t length=st.st_size; char etag[64]; onion_shortcut_etag(&st, etag); const char *range=onion_request_get_header(request, "Range"); if (range){ strncat(etag,range,sizeof(etag)-1); } onion_response_set_header(res, "Etag", etag); if (range && strncmp(range,"bytes=",6)==0){ onion_response_set_code(res, HTTP_PARTIAL_CONTENT); //ONION_DEBUG("Need just a range: %s",range); char tmp[1024]; strncpy(tmp, range+6, 1024); char *start=tmp; char *end=tmp; while (*end!='-' && *end) end++; if (*end=='-'){ *end='\0'; end++; //ONION_DEBUG("Start %s, end %s",start,end); size_t ends, starts; if (*end) ends=atol(end); else ends=length; starts=atol(start); length=ends-starts+1; lseek(fd, starts, SEEK_SET); snprintf(tmp,sizeof(tmp),"bytes %d-%d/%d",(unsigned int)starts, (unsigned int)ends, (unsigned int)st.st_size); //onion_response_set_header(res, "Accept-Ranges","bytes"); onion_response_set_header(res, "Content-Range",tmp); } } onion_response_set_length(res, length); onion_response_set_header(res, "Content-Type", onion_mime_get(filename) ); ONION_DEBUG("Mime type is %s",onion_mime_get(filename)); ONION_DEBUG0("Etag %s", etag); const char *prev_etag=onion_request_get_header(request, "If-None-Match"); if (prev_etag && (strcmp(prev_etag, etag)==0)){ ONION_DEBUG0("Not modified"); onion_response_set_length(res, 0); onion_response_set_code(res, HTTP_NOT_MODIFIED); onion_response_write_headers(res); close(fd); return OCS_PROCESSED; } onion_response_write_headers(res); if ((onion_request_get_flags(request)&OR_HEAD) == OR_HEAD){ // Just head. length=0; } if (length){ #ifdef USE_SENDFILE if (request->connection.listen_point->write==(void*)onion_http_write){ // Lets have a house party! I can use sendfile! onion_response_write(res,NULL,0); ONION_DEBUG("Using sendfile"); int r=sendfile(request->connection.fd, fd, NULL, length); ONION_DEBUG("Wrote %d, should be %d (%s)", r, length, r==length ? "ok" : "nok"); if (r!=length || r<0){ ONION_ERROR("Could not send all file (%s)", strerror(errno)); close(fd); return OCS_INTERNAL_ERROR; } res->sent_bytes+=length; res->sent_bytes_total+=length; } else #endif { // Ok, no I cant, do it as always. int r=0,w; size_t tr=0; char tmp[4046]; if (length>sizeof(tmp)){ size_t max=length-sizeof(tmp); while( tr<max ){ r=read(fd,tmp,sizeof(tmp)); tr+=r; if (r<0) break; w=onion_response_write(res, tmp, r); if (w!=r){ ONION_ERROR("Wrote less than read: write %d, read %d. Quite probably closed connection.",w,r); break; } } } if (sizeof(tmp) >= (length-tr)){ r=read(fd, tmp, length-tr); w=onion_response_write(res, tmp, r); if (w!=r){ ONION_ERROR("Wrote less than read: write %d, read %d. Quite probably closed connection.",w,r); } } } } close(fd); return OCS_PROCESSED; }