#include #include #include #include #include #include #include #include #include #include #include #include #include #include void send_answer(void * new_socket) { FILE * f = fdopen((size_t) new_socket, "w"); puts("> sending answer..."); fputs("HTTP/1.0 200 OK\n", f); fputs("content-type: text/html\n\n", f); fputs("", f); fputs("", f); fputs("test", f); fputs("", f); fputs("", f); fputs("
", f); fputs("
\n", f); fputs("", f); fputs("", f); fputs("
", f); fputs("\n\0", f); fflush(f); puts("> answer sent."); fclose(f); } // #define FOREACH_HEADER(HEADER) \ // HEADER("Content-Type: multipart/form-data; boundary=") // #define GENERATE_HEADERS(H) sizeof(H), H // // typedef struct { // const int length; // const char * string; // } length_value; // // const length_value headers[] = { // FOREACH_HEADER(GENERATE_HEADERS) // }; void * next_customer(void * new_socket) { if (new_socket == 0) return NULL; const int MAX_HEADER_LINE_LENGTH = 1024; const int MAX_BOUNDARY_LENGTH = 64; char boundary[64] = {0}; int boundary_size = 0; usleep(100); // wait to avoid poll, epoll or evil select FILE * f = fdopen((size_t) new_socket, "r"); char buffer[MAX_HEADER_LINE_LENGTH]; while(fgets(buffer, MAX_HEADER_LINE_LENGTH, f)) { printf("%s [%d]", buffer, errno); if(buffer[0] == '\n' || (buffer[0] == '\r' && buffer[1] == '\n')) { // either LF or CR/LF (windows) puts("END HEADER\n"); if (!boundary[0]) break; continue; } const char header_multipart[] = "Content-Type: multipart/form-data; boundary="; if (!boundary[0] && 0 == strncasecmp(buffer, header_multipart, sizeof(header_multipart) - 1)) { for(int i=sizeof(header_multipart); i NEXT CHUNK: %s\n", buffer); i = 0; } break; } } } else { printf("%s", buffer); } // I think another if could now search for "Content-Disposition: form-data; " and store the remaining fields // somewhere, e.g. name="okay"; filename="1.gif". Also there may also be a content-type per chunk, which we // could also add and erase, where I have currently NEXT CHUNK. // After that the text output (printf("%s", buffer)) will only contain content without headers, so that printf // can then be replaced with some fwrite to a memstream and we are done :) } puts("> sending answer..."); send_answer(new_socket); puts("> answer sent."); puts("> file closed."); //close((size_t) new_socket); // pthread_exit(EXIT_SUCCESS); return NULL; } void serve(int server_fd) { struct sockaddr_in address; int addrlen = sizeof(address); warn("waiting for connections on %d", server_fd); for(size_t new_socket = 0 ; 1 ; new_socket=accept(server_fd, (struct sockaddr*) &address, (socklen_t*) &addrlen)) { warn("next: %ld\n", new_socket); // set non blocking mode... fcntl((size_t) new_socket, F_SETFL, fcntl((size_t) new_socket, F_GETFL) | O_NONBLOCK); next_customer((void*) new_socket); // pthread_t thread_id; // pthread_create(&thread_id, NULL, next_customer, (void*) new_socket); // pthread_join(thread_id, NULL); } } #define PORT 8080 int main(int argc, char const *argv[]) { int server_fd; int opt = 1; struct sockaddr_in address; address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(PORT); 0 == (server_fd = socket(AF_INET, SOCK_STREAM, 0)) ? err(EXIT_FAILURE, NULL) : setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)) ? err(EXIT_FAILURE, "setsockopt failed on socket with fileno %d", server_fd) : bind(server_fd, (struct sockaddr*) &address, sizeof(address)) ? err(EXIT_FAILURE, NULL) : listen(server_fd, SOMAXCONN) ? err(EXIT_FAILURE, NULL) : serve(server_fd); return 0; } // vim: shiftwidth=2 tabstop=2 number