@@ -92,10 +92,15 @@ struct { int present; char host[FILED_PATH_BUFFER_SIZE]; } host; + + enum { + FILED_CONNECTION_CLOSE, + FILED_CONNECTION_KEEP_ALIVE + } connection; } headers; }; /* Log record */ struct filed_log_entry { @@ -687,10 +692,11 @@ range_start = 0; range_end = 0; range_request = 0; range_length = -1; buffer_st->headers.host.present = 0; + buffer_st->headers.connection = FILED_CONNECTION_CLOSE; buffer = buffer_st->tmpbuf; buffer_len = sizeof(buffer_st->tmpbuf); fgets_ret = fgets(buffer, buffer_len, fp); @@ -786,10 +792,12 @@ while (*workbuffer == ' ') { workbuffer++; } strcpy(buffer_st->headers.host.host, workbuffer); + } else if (strncasecmp(buffer, "Connection: Keep-Alive", 22) == 0) { + buffer_st->headers.connection = FILED_CONNECTION_KEEP_ALIVE; } if (memcmp(buffer, "\r\n", 2) == 0) { break; } @@ -889,13 +897,25 @@ return; /* Currently unused: path */ path = path; } + +/* Convert an enum representing the "Connection" header value to a string */ +static const char *filed_connection_str(int connection_value) { + switch (connection_value) { + case FILED_CONNECTION_CLOSE: + return("close"); + case FILED_CONNECTION_KEEP_ALIVE: + return("keep-alive"); + } + + return("close"); +} /* Handle a single request from a client */ -static void filed_handle_client(int fd, struct filed_http_request *request, struct filed_log_entry *log, struct filed_options *options) { +static int filed_handle_client(int fd, struct filed_http_request *request, struct filed_log_entry *log, struct filed_options *options) { struct filed_fileinfo *fileinfo; ssize_t sendfile_ret; size_t sendfile_size; off_t sendfile_offset, sendfile_sent, sendfile_len; char *path; @@ -915,21 +935,21 @@ log->http_code = -1; log->reason = "fdopen_failed"; filed_log_entry(log); - return; + return(0); } request = filed_get_http_request(fp, request, options); if (request == NULL) { log->buffer[0] = '\0'; filed_error_page(fp, date_current, 500, FILED_REQUEST_METHOD_GET, "format", log); - return; + return(0); } path = request->path; strcpy(log->buffer, path); log->method = request->method; @@ -936,28 +956,28 @@ /* If the requested path is a directory, redirect to index page */ if (request->type == FILED_REQUEST_TYPE_DIRECTORY) { filed_redirect_index(fp, date_current, path, log); - return; + return(0); } fileinfo = filed_open_file(path, &request->fileinfo); if (fileinfo == NULL) { filed_error_page(fp, date_current, 404, request->method, "open_failed", log); - return; + return(0); } if (request->headers.range.present) { if (request->headers.range.offset != 0 || request->headers.range.length >= 0) { if (request->headers.range.offset >= fileinfo->len) { filed_error_page(fp, date_current, 416, request->method, "range_invalid", log); close(fileinfo->fd); - return; + return(0); } if (request->headers.range.length == ((off_t) -1)) { filed_log_msg_debug("Computing length to fit in bounds: fileinfo->len = %llu, request->headers.range.offset = %llu", (unsigned long long) fileinfo->len, @@ -981,16 +1001,17 @@ /* Compute fake range parameters that includes the entire file */ request->headers.range.offset = 0; request->headers.range.length = fileinfo->len; } - fprintf(fp, "HTTP/1.1 %i OK\r\nDate: %s\r\nServer: filed\r\nLast-Modified: %s\r\nContent-Length: %llu\r\nAccept-Ranges: bytes\r\nContent-Type: %s\r\nConnection: close\r\nETag: \"%s\"\r\n", + fprintf(fp, "HTTP/1.1 %i OK\r\nDate: %s\r\nServer: filed\r\nLast-Modified: %s\r\nContent-Length: %llu\r\nAccept-Ranges: bytes\r\nContent-Type: %s\r\nConnection: %s\r\nETag: \"%s\"\r\n", http_code, date_current, fileinfo->lastmod, (unsigned long long) request->headers.range.length, fileinfo->type, + filed_connection_str(request->headers.connection), fileinfo->etag ); if (http_code == 206) { fprintf(fp, "Content-Range: bytes %llu-%llu/%llu\r\n", @@ -1077,15 +1098,19 @@ log->endtime = (time_t) -1; log->sent_length = sendfile_sent; filed_log_entry(log); - close(fileinfo->fd); + if (request->headers.connection != FILED_CONNECTION_KEEP_ALIVE) { + close(fileinfo->fd); - fclose(fp); + fclose(fp); - return; + return(0); + } + + return(1); } /* Handle incoming connections */ static void *filed_worker_thread(void *arg_v) { struct filed_worker_thread_args *arg; @@ -1093,11 +1118,12 @@ struct filed_log_entry *log, local_dummy_log; struct filed_options *options; struct sockaddr_in6 addr; socklen_t addrlen; int failure_count = 0, max_failure_count = FILED_MAX_FAILURE_COUNT; - int master_fd, fd; + int accept_new = 1; + int master_fd, fd = -1; /* Read arguments */ arg = arg_v; master_fd = arg->fd; @@ -1118,12 +1144,14 @@ } log->type = FILED_LOG_TYPE_TRANSFER; /* Accept a new client */ - addrlen = sizeof(addr); - fd = accept(master_fd, (struct sockaddr *) &addr, &addrlen); + if (accept_new) { + addrlen = sizeof(addr); + fd = accept(master_fd, (struct sockaddr *) &addr, &addrlen); + } /* * If we fail, make a note of it so we don't go into a loop of * accept() failing */ @@ -1148,11 +1176,11 @@ /* Reset failure count*/ failure_count = 0; /* Handle socket */ - filed_handle_client(fd, &request, log, options); + accept_new = !filed_handle_client(fd, &request, log, options); } /* Report error */ filed_log_msg("THREAD_DIED ABNORMAL");