Commits

Aristarkh Zagorodnikov  committed 4d59037

Allow zero-length chunks (this rarely happens but appears to be completely valid)
Improved resource cleanup for systems with asynchronous exceptions
Minor optimizations

  • Participants
  • Parent commits 36e08c3
  • Branches v0.2

Comments (0)

Files changed (1)

File gridfs/mod_gridfs.cpp

 	const gridfs_config *const config = static_cast<gridfs_config *>(ap_get_module_config(request->per_dir_config, &gridfs_module));
 	if (config->connection_string == 0 || config->database == 0)
 		return DECLINED;
-    request->allowed |= AP_METHOD_BIT << M_GET;
+   	request->allowed |= AP_METHOD_BIT << M_GET;
 	if (request->method_number != M_GET)
 		return HTTP_NOT_IMPLEMENTED;
 	if (*request->uri != '/')
 	try
 	{
 		std::auto_ptr<mongo::ScopedDbConnection> connection(mongo::ScopedDbConnection::getScopedDbConnection(*config->connection_string, config->connect_timeout));
-		try
+		const mongo::GridFS gridfs(connection->conn(), *config->database);
+		mongo::GridFile gridfile = gridfs.findFile(filename, config->slave_ok);
+		if (!gridfile.exists())
 		{
-			const mongo::GridFS gridfs(connection->conn(), *config->database);
-			mongo::GridFile gridfile = gridfs.findFile(filename, config->slave_ok);
-			if (!gridfile.exists())
+			if (config->slave_ok)
 			{
-				if (config->slave_ok)
-				{
-					gridfile = gridfs.findFile(filename, false);
-					if (!gridfile.exists())
-					{
-						connection->done();
-						return HTTP_NOT_FOUND;
-					}
-				}
-				else
+				gridfile = gridfs.findFile(filename, false);
+				if (!gridfile.exists())
 				{
 					connection->done();
 					return HTTP_NOT_FOUND;
 				}
 			}
-			const mongo::gridfs_offset file_length = gridfile.getContentLength();
-			const mongo::Date_t upload_date = gridfile.getUploadDate();
-			const std::string& md5 = gridfile.getMD5();
-			const std::string& content_type = gridfile.getContentType();
-			request->mtime = apr_time_from_sec(upload_date.toTimeT());
-			ap_set_last_modified(request);
+			else
+			{
+				connection->done();
+				return HTTP_NOT_FOUND;
+			}
+		}
+		const mongo::gridfs_offset file_length = gridfile.getContentLength();
+		const mongo::Date_t upload_date = gridfile.getUploadDate();
+		const std::string& md5 = gridfile.getMD5();
+		request->mtime = apr_time_from_sec(upload_date.toTimeT());
+		ap_set_last_modified(request);
+		if (!md5.empty())
+			apr_table_set(request->headers_out, "Etag", md5.c_str());
+		if (ap_meets_conditions(request) == HTTP_NOT_MODIFIED)
+		{
 			if (!md5.empty())
-				apr_table_set(request->headers_out, "Etag", md5.c_str());
-			if (ap_meets_conditions(request) == HTTP_NOT_MODIFIED)
+				apr_table_unset(request->headers_out, "Etag");
+			connection->done();
+			return HTTP_NOT_MODIFIED;
+		}
+		const std::string& content_type = gridfile.getContentType();
+		if (file_length != 0 && request->header_only == 0)
+		{
+			const int num_chunks = gridfile.getNumChunks();
+			if (num_chunks == 0)
 			{
-				if (!md5.empty())
-					apr_table_unset(request->headers_out, "Etag");
+				ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, request, "mod_gridfs: No chunks available for file '%s'.", filename); 
 				connection->done();
-				return HTTP_NOT_MODIFIED;
+				return HTTP_INTERNAL_SERVER_ERROR;
 			}
-			if (file_length != 0 && request->header_only == 0)
+			brigade = apr_brigade_create(request->pool, request->connection->bucket_alloc);
+			if (brigade == 0)
 			{
-				const int num_chunks = gridfile.getNumChunks();
-				if (num_chunks == 0)
+				ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, request, "mod_gridfs: Failed to create brigade."); 
+				connection->done();
+				return HTTP_INTERNAL_SERVER_ERROR;
+			}
+			apr_bucket_brigade * const destroy_brigade = brigade;
+			for (int chunk_index = 0;chunk_index < num_chunks;chunk_index++) 
+			{
+				const mongo::GridFSChunk& chunk = gridfile.getChunk(chunk_index);
+				int chunk_length;
+				const char *chunk_data = chunk.data(chunk_length);
+				if (chunk_length == 0)
+					continue;
+				if ((result = apr_brigade_write(brigade, 0, 0, chunk_data, chunk_length)) != APR_SUCCESS)
 				{
-					ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, request, "mod_gridfs: No chunks available for file '%s'.", filename); 
+					ap_log_rerror(APLOG_MARK, APLOG_ERR, result, request, "mod_gridfs: Failed to write chunk %d for file '%s' to brigade (length: %d).", chunk_index, filename, chunk_length); 
+					brigade = 0;
+					apr_brigade_destroy(destroy_brigade);
 					connection->done();
 					return HTTP_INTERNAL_SERVER_ERROR;
 				}
-				brigade = apr_brigade_create(request->pool, request->connection->bucket_alloc);
-				if (brigade == 0)
-				{
-					ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, request, "mod_gridfs: Failed to create brigade."); 
-					connection->done();
-					return HTTP_INTERNAL_SERVER_ERROR;
-				}
-				for (int chunk_index = 0;chunk_index < num_chunks;chunk_index++) 
-				{
-					const mongo::GridFSChunk& chunk = gridfile.getChunk(chunk_index);
-					int chunk_length;
-					const char *chunk_data = chunk.data(chunk_length);
-					if (chunk_length == 0)
-					{
-						ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, request, "mod_gridfs: Empty chunk %d for file '%s'.", chunk_index, filename); 
-						connection->done();
-						return HTTP_INTERNAL_SERVER_ERROR;
-					}
-					if ((result = apr_brigade_write(brigade, 0, 0, chunk_data, chunk_length)) != APR_SUCCESS)
-					{
-						ap_log_rerror(APLOG_MARK, APLOG_ERR, result, request, "mod_gridfs: Failed to write chunk %d for file '%s' to brigade (length: %d).", chunk_index, filename, chunk_length); 
-						connection->done();
-						apr_brigade_destroy(brigade);
-						return HTTP_INTERNAL_SERVER_ERROR;
-					}
-				}
 			}
-			ap_set_content_length(request, file_length);
-			if (content_type.empty())
-			{
-				request->finfo.filetype = APR_REG;
-				request->filename = const_cast<char *>(filename);
-				if ((result = ap_run_type_checker(request)) != APR_SUCCESS)
-					ap_log_rerror(APLOG_MARK, APLOG_WARNING, result, request, "mod_gridfs: Failed to run type checker for file '%s'.", filename); 
-			}
-			else
-				ap_set_content_type(request, content_type.c_str());
-		}
-		catch (...)
-		{
-			try
-			{
-				connection->done();
-			}
-			catch (...)
-			{
-			}
-			if (brigade != 0)
-				apr_brigade_destroy(brigade);
-			throw;
 		}
 		connection->done();
+		ap_set_content_length(request, file_length);
+		if (content_type.empty())
+		{
+			request->finfo.filetype = APR_REG;
+			request->filename = const_cast<char *>(filename);
+			if ((result = ap_run_type_checker(request)) != APR_SUCCESS)
+				ap_log_rerror(APLOG_MARK, APLOG_WARNING, result, request, "mod_gridfs: Failed to run type checker for file '%s'.", filename); 
+		}
+		else
+			ap_set_content_type(request, content_type.c_str());
+		if (config->cache_max_age != 0)
+		{
+			char cache_control[32];
+			snprintf(cache_control, sizeof(cache_control) - 1, "public, max-age=%d", config->cache_max_age);
+			apr_table_set(request->headers_out, "Cache-Control", cache_control);
+			apr_time_t expires_time = request->request_time + apr_time_from_sec(config->cache_max_age);
+			char expires[APR_RFC822_DATE_LEN];
+			apr_rfc822_date(expires, expires_time);
+			apr_table_set(request->headers_out, "Expires", expires);
+		}
 	}
 	catch (const std::exception& exception)
 	{
 		ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, request, "mod_gridfs: Handler exception occured for file '%s': %s.", filename, exception.what()); 
+		if (brigade != 0)
+			apr_brigade_destroy(brigade);
 		return HTTP_INTERNAL_SERVER_ERROR;
 	}
 	catch (...)
 	{
 		ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, request, "mod_gridfs: Unknown handler exception occured for file '%s'.", filename); 
+		if (brigade != 0)
+			apr_brigade_destroy(brigade);
 		return HTTP_INTERNAL_SERVER_ERROR;
 	}
-	if (config->cache_max_age != 0)
-	{
-		char cache_control[32];
-		snprintf(cache_control, sizeof(cache_control) - 1, "public, max-age=%d", config->cache_max_age);
-		apr_table_set(request->headers_out, "Cache-Control", cache_control);
-		apr_time_t expires_time = request->request_time + apr_time_from_sec(config->cache_max_age);
-		char expires[APR_RFC822_DATE_LEN];
-		apr_rfc822_date(expires, expires_time);
-		apr_table_set(request->headers_out, "Expires", expires);
-	}
 	if (brigade != 0)
 	    return ap_pass_brigade(request->output_filters, brigade);
 	return OK;