itsi 0.2.16 → 0.2.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.zed/settings.json +32 -0
- data/CHANGELOG.md +21 -0
- data/Cargo.lock +4 -2
- data/crates/itsi_acme/Cargo.toml +1 -1
- data/crates/itsi_scheduler/Cargo.toml +1 -1
- data/crates/itsi_server/Cargo.toml +3 -1
- data/crates/itsi_server/src/lib.rs +6 -1
- data/crates/itsi_server/src/ruby_types/itsi_body_proxy/mod.rs +2 -0
- data/crates/itsi_server/src/ruby_types/itsi_grpc_call.rs +4 -4
- data/crates/itsi_server/src/ruby_types/itsi_grpc_response_stream/mod.rs +14 -13
- data/crates/itsi_server/src/ruby_types/itsi_http_request.rs +64 -33
- data/crates/itsi_server/src/ruby_types/itsi_http_response.rs +151 -152
- data/crates/itsi_server/src/ruby_types/itsi_server/file_watcher.rs +422 -110
- data/crates/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +62 -15
- data/crates/itsi_server/src/ruby_types/itsi_server.rs +1 -1
- data/crates/itsi_server/src/server/binds/listener.rs +45 -7
- data/crates/itsi_server/src/server/frame_stream.rs +142 -0
- data/crates/itsi_server/src/server/http_message_types.rs +142 -9
- data/crates/itsi_server/src/server/io_stream.rs +28 -5
- data/crates/itsi_server/src/server/lifecycle_event.rs +1 -1
- data/crates/itsi_server/src/server/middleware_stack/middlewares/auth_basic.rs +2 -3
- data/crates/itsi_server/src/server/middleware_stack/middlewares/compression.rs +8 -10
- data/crates/itsi_server/src/server/middleware_stack/middlewares/cors.rs +2 -3
- data/crates/itsi_server/src/server/middleware_stack/middlewares/csp.rs +3 -3
- data/crates/itsi_server/src/server/middleware_stack/middlewares/error_response/default_responses.rs +54 -56
- data/crates/itsi_server/src/server/middleware_stack/middlewares/error_response.rs +5 -7
- data/crates/itsi_server/src/server/middleware_stack/middlewares/etag.rs +5 -5
- data/crates/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +7 -10
- data/crates/itsi_server/src/server/middleware_stack/middlewares/redirect.rs +2 -3
- data/crates/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +1 -2
- data/crates/itsi_server/src/server/middleware_stack/middlewares/static_response.rs +4 -6
- data/crates/itsi_server/src/server/mod.rs +1 -0
- data/crates/itsi_server/src/server/process_worker.rs +3 -4
- data/crates/itsi_server/src/server/serve_strategy/acceptor.rs +16 -12
- data/crates/itsi_server/src/server/serve_strategy/cluster_mode.rs +83 -31
- data/crates/itsi_server/src/server/serve_strategy/single_mode.rs +166 -142
- data/crates/itsi_server/src/server/signal.rs +37 -9
- data/crates/itsi_server/src/server/thread_worker.rs +84 -69
- data/crates/itsi_server/src/services/itsi_http_service.rs +43 -43
- data/crates/itsi_server/src/services/static_file_server.rs +28 -47
- data/docs/benchmark-dashboard/.gitignore +27 -0
- data/docs/benchmark-dashboard/app/api/benchmarks/route.ts +22 -0
- data/docs/benchmark-dashboard/app/globals.css +94 -0
- data/docs/benchmark-dashboard/app/layout.tsx +20 -0
- data/docs/benchmark-dashboard/app/page.tsx +252 -0
- data/docs/benchmark-dashboard/components/benchmark-dashboard.tsx +1663 -0
- data/docs/benchmark-dashboard/components/theme-provider.tsx +11 -0
- data/docs/benchmark-dashboard/components/ui/accordion.tsx +58 -0
- data/docs/benchmark-dashboard/components/ui/alert-dialog.tsx +141 -0
- data/docs/benchmark-dashboard/components/ui/alert.tsx +59 -0
- data/docs/benchmark-dashboard/components/ui/aspect-ratio.tsx +7 -0
- data/docs/benchmark-dashboard/components/ui/avatar.tsx +50 -0
- data/docs/benchmark-dashboard/components/ui/badge.tsx +36 -0
- data/docs/benchmark-dashboard/components/ui/breadcrumb.tsx +115 -0
- data/docs/benchmark-dashboard/components/ui/button.tsx +56 -0
- data/docs/benchmark-dashboard/components/ui/calendar.tsx +66 -0
- data/docs/benchmark-dashboard/components/ui/card.tsx +79 -0
- data/docs/benchmark-dashboard/components/ui/carousel.tsx +262 -0
- data/docs/benchmark-dashboard/components/ui/chart.tsx +365 -0
- data/docs/benchmark-dashboard/components/ui/checkbox.tsx +30 -0
- data/docs/benchmark-dashboard/components/ui/collapsible.tsx +11 -0
- data/docs/benchmark-dashboard/components/ui/command.tsx +153 -0
- data/docs/benchmark-dashboard/components/ui/context-menu.tsx +200 -0
- data/docs/benchmark-dashboard/components/ui/dialog.tsx +122 -0
- data/docs/benchmark-dashboard/components/ui/drawer.tsx +118 -0
- data/docs/benchmark-dashboard/components/ui/dropdown-menu.tsx +200 -0
- data/docs/benchmark-dashboard/components/ui/form.tsx +178 -0
- data/docs/benchmark-dashboard/components/ui/hover-card.tsx +29 -0
- data/docs/benchmark-dashboard/components/ui/input-otp.tsx +71 -0
- data/docs/benchmark-dashboard/components/ui/input.tsx +22 -0
- data/docs/benchmark-dashboard/components/ui/label.tsx +26 -0
- data/docs/benchmark-dashboard/components/ui/loading-spinner.tsx +12 -0
- data/docs/benchmark-dashboard/components/ui/menubar.tsx +236 -0
- data/docs/benchmark-dashboard/components/ui/navigation-menu.tsx +128 -0
- data/docs/benchmark-dashboard/components/ui/pagination.tsx +117 -0
- data/docs/benchmark-dashboard/components/ui/popover.tsx +31 -0
- data/docs/benchmark-dashboard/components/ui/progress.tsx +28 -0
- data/docs/benchmark-dashboard/components/ui/radio-group.tsx +44 -0
- data/docs/benchmark-dashboard/components/ui/resizable.tsx +45 -0
- data/docs/benchmark-dashboard/components/ui/scroll-area.tsx +48 -0
- data/docs/benchmark-dashboard/components/ui/select.tsx +160 -0
- data/docs/benchmark-dashboard/components/ui/separator.tsx +31 -0
- data/docs/benchmark-dashboard/components/ui/sheet.tsx +140 -0
- data/docs/benchmark-dashboard/components/ui/sidebar.tsx +763 -0
- data/docs/benchmark-dashboard/components/ui/skeleton.tsx +15 -0
- data/docs/benchmark-dashboard/components/ui/slider.tsx +28 -0
- data/docs/benchmark-dashboard/components/ui/sonner.tsx +31 -0
- data/docs/benchmark-dashboard/components/ui/switch.tsx +29 -0
- data/docs/benchmark-dashboard/components/ui/table.tsx +117 -0
- data/docs/benchmark-dashboard/components/ui/tabs.tsx +55 -0
- data/docs/benchmark-dashboard/components/ui/textarea.tsx +22 -0
- data/docs/benchmark-dashboard/components/ui/toast.tsx +129 -0
- data/docs/benchmark-dashboard/components/ui/toaster.tsx +35 -0
- data/docs/benchmark-dashboard/components/ui/toggle-group.tsx +61 -0
- data/docs/benchmark-dashboard/components/ui/toggle.tsx +45 -0
- data/docs/benchmark-dashboard/components/ui/tooltip.tsx +30 -0
- data/docs/benchmark-dashboard/components/ui/use-mobile.tsx +19 -0
- data/docs/benchmark-dashboard/components/ui/use-toast.ts +194 -0
- data/docs/benchmark-dashboard/components.json +21 -0
- data/docs/benchmark-dashboard/dist/benchmark-dashboard.css +1 -0
- data/docs/benchmark-dashboard/dist/benchmark-dashboard.iife.js +211 -0
- data/docs/benchmark-dashboard/dist/placeholder-logo.png +0 -0
- data/docs/benchmark-dashboard/dist/placeholder-logo.svg +1 -0
- data/docs/benchmark-dashboard/dist/placeholder-user.jpg +0 -0
- data/docs/benchmark-dashboard/dist/placeholder.jpg +0 -0
- data/docs/benchmark-dashboard/dist/placeholder.svg +1 -0
- data/docs/benchmark-dashboard/embed.tsx +13 -0
- data/docs/benchmark-dashboard/hooks/use-mobile.tsx +19 -0
- data/docs/benchmark-dashboard/hooks/use-toast.ts +194 -0
- data/docs/benchmark-dashboard/lib/benchmark-utils.ts +54 -0
- data/docs/benchmark-dashboard/lib/utils.ts +6 -0
- data/docs/benchmark-dashboard/next.config.mjs +14 -0
- data/docs/benchmark-dashboard/package-lock.json +5859 -0
- data/docs/benchmark-dashboard/package.json +72 -0
- data/docs/benchmark-dashboard/pnpm-lock.yaml +5 -0
- data/docs/benchmark-dashboard/postcss.config.mjs +8 -0
- data/docs/benchmark-dashboard/styles/globals.css +94 -0
- data/docs/benchmark-dashboard/tailwind.config.ts +96 -0
- data/docs/benchmark-dashboard/tsconfig.json +27 -0
- data/docs/benchmark-dashboard/vite.config.ts +24 -0
- data/docs/build.rb +52 -0
- data/docs/content/acknowledgements/_index.md +1 -1
- data/docs/content/benchmarks/index.md +96 -0
- data/docs/content/configuration/_index.md +2 -2
- data/docs/content/getting_started/_index.md +76 -46
- data/docs/content/itsi_scheduler/_index.md +2 -2
- data/docs/hugo.yaml +10 -1
- data/docs/static/results.json +1 -0
- data/docs/static/scripts/benchmark-dashboard.iife.js +211 -0
- data/docs/static/styles/benchmark-dashboard.css +1 -0
- data/examples/api_with_schema_and_controllers/README.md +1 -1
- data/gems/scheduler/Cargo.lock +1 -1
- data/gems/scheduler/lib/itsi/scheduler/version.rb +1 -1
- data/gems/server/Cargo.lock +3 -1
- data/gems/server/exe/itsi +8 -5
- data/gems/server/lib/itsi/http_request.rb +31 -39
- data/gems/server/lib/itsi/http_response.rb +5 -0
- data/gems/server/lib/itsi/rack_env_pool.rb +59 -0
- data/gems/server/lib/itsi/server/config/dsl.rb +6 -4
- data/gems/server/lib/itsi/server/config/middleware/compression.md +3 -3
- data/gems/server/lib/itsi/server/config/middleware/endpoint/controller.md +1 -1
- data/gems/server/lib/itsi/server/config/middleware/proxy.md +2 -2
- data/gems/server/lib/itsi/server/config/middleware/proxy.rb +1 -1
- data/gems/server/lib/itsi/server/config/middleware/rackup_file.rb +2 -2
- data/gems/server/lib/itsi/server/config/options/auto_reload_config.rb +11 -6
- data/gems/server/lib/itsi/server/config/options/include.md +1 -0
- data/gems/server/lib/itsi/server/config/options/include.rb +13 -8
- data/gems/server/lib/itsi/server/config/options/pipeline_flush.md +16 -0
- data/gems/server/lib/itsi/server/config/options/pipeline_flush.rb +19 -0
- data/gems/server/lib/itsi/server/config/options/reuse_port.rb +2 -4
- data/gems/server/lib/itsi/server/config/options/writev.md +25 -0
- data/gems/server/lib/itsi/server/config/options/writev.rb +19 -0
- data/gems/server/lib/itsi/server/config.rb +22 -9
- data/gems/server/lib/itsi/server/default_config/Itsi.rb +9 -8
- data/gems/server/lib/itsi/server/grpc/grpc_call.rb +2 -0
- data/gems/server/lib/itsi/server/grpc/grpc_interface.rb +2 -2
- data/gems/server/lib/itsi/server/rack/handler/itsi.rb +3 -1
- data/gems/server/lib/itsi/server/rack_interface.rb +17 -12
- data/gems/server/lib/itsi/server/scheduler_interface.rb +2 -0
- data/gems/server/lib/itsi/server/version.rb +1 -1
- data/gems/server/lib/itsi/server.rb +1 -0
- data/gems/server/lib/ruby_lsp/itsi/addon.rb +12 -13
- data/gems/server/test/helpers/test_helper.rb +12 -13
- data/gems/server/test/middleware/grpc/grpc.rb +13 -14
- data/gems/server/test/middleware/grpc/test_service_impl.rb +3 -3
- data/gems/server/test/middleware/proxy.rb +262 -268
- data/lib/itsi/version.rb +1 -1
- metadata +97 -6
- data/tasks.txt +0 -28
@@ -2,7 +2,7 @@ use crate::{
|
|
2
2
|
default_responses::NOT_FOUND_RESPONSE,
|
3
3
|
prelude::*,
|
4
4
|
server::{
|
5
|
-
http_message_types::{HttpRequest, HttpResponse, RequestExt, ResponseFormat},
|
5
|
+
http_message_types::{HttpBody, HttpRequest, HttpResponse, RequestExt, ResponseFormat},
|
6
6
|
middleware_stack::ErrorResponse,
|
7
7
|
redirect_type::RedirectType,
|
8
8
|
},
|
@@ -16,7 +16,6 @@ use http::{
|
|
16
16
|
},
|
17
17
|
HeaderName, HeaderValue, Response, StatusCode,
|
18
18
|
};
|
19
|
-
use http_body_util::{combinators::BoxBody, Full};
|
20
19
|
use itsi_error::Result;
|
21
20
|
use parking_lot::{Mutex, RwLock};
|
22
21
|
use percent_encoding::percent_decode_str;
|
@@ -28,7 +27,6 @@ use std::{
|
|
28
27
|
borrow::Cow,
|
29
28
|
cmp::Ordering,
|
30
29
|
collections::HashMap,
|
31
|
-
convert::Infallible,
|
32
30
|
fs::Metadata,
|
33
31
|
ops::Deref,
|
34
32
|
path::{Path, PathBuf},
|
@@ -324,7 +322,7 @@ impl StaticFileServer {
|
|
324
322
|
}) => Response::builder()
|
325
323
|
.status(StatusCode::MOVED_PERMANENTLY)
|
326
324
|
.header(header::LOCATION, redirect_to)
|
327
|
-
.body(
|
325
|
+
.body(HttpBody::empty())
|
328
326
|
.unwrap(),
|
329
327
|
Err(not_found_behavior) => match not_found_behavior {
|
330
328
|
NotFoundBehavior::Error(error_response) => {
|
@@ -340,7 +338,7 @@ impl StaticFileServer {
|
|
340
338
|
NotFoundBehavior::Redirect(redirect) => Response::builder()
|
341
339
|
.status(redirect.r#type.status_code())
|
342
340
|
.header(header::LOCATION, redirect.to)
|
343
|
-
.body(
|
341
|
+
.body(HttpBody::empty())
|
344
342
|
.unwrap(),
|
345
343
|
},
|
346
344
|
})
|
@@ -407,7 +405,7 @@ impl StaticFileServer {
|
|
407
405
|
|
408
406
|
Response::builder()
|
409
407
|
.status(StatusCode::NOT_FOUND)
|
410
|
-
.body(
|
408
|
+
.body(HttpBody::empty())
|
411
409
|
.unwrap()
|
412
410
|
}
|
413
411
|
|
@@ -648,15 +646,8 @@ impl StaticFileServer {
|
|
648
646
|
Err(nf)
|
649
647
|
}
|
650
648
|
|
651
|
-
async fn stream_file_range(
|
652
|
-
&self,
|
653
|
-
path: PathBuf,
|
654
|
-
start: u64,
|
655
|
-
end: u64,
|
656
|
-
) -> Option<BoxBody<Bytes, Infallible>> {
|
649
|
+
async fn stream_file_range(&self, path: PathBuf, start: u64, end: u64) -> Option<HttpBody> {
|
657
650
|
use futures::TryStreamExt;
|
658
|
-
use http_body_util::StreamBody;
|
659
|
-
use hyper::body::Frame;
|
660
651
|
use tokio::io::AsyncSeekExt;
|
661
652
|
use tokio_util::io::ReaderStream;
|
662
653
|
|
@@ -687,32 +678,25 @@ impl StaticFileServer {
|
|
687
678
|
let range_length = end - start + 1;
|
688
679
|
let limited_reader = tokio::io::AsyncReadExt::take(file, range_length);
|
689
680
|
let path_clone = path.clone();
|
690
|
-
let stream = ReaderStream::with_capacity(limited_reader, 64 * 1024)
|
691
|
-
.
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
});
|
696
|
-
|
697
|
-
Some(BoxBody::new(StreamBody::new(stream)))
|
681
|
+
let stream = ReaderStream::with_capacity(limited_reader, 64 * 1024).map_err(move |e| {
|
682
|
+
warn!("Error streaming file {}: {}", path_clone.display(), e);
|
683
|
+
unreachable!("We handle IO errors above")
|
684
|
+
});
|
685
|
+
Some(HttpBody::stream(stream))
|
698
686
|
}
|
699
687
|
|
700
|
-
async fn stream_file(&self, path: PathBuf) -> Option<
|
688
|
+
async fn stream_file(&self, path: PathBuf) -> Option<HttpBody> {
|
701
689
|
use futures::TryStreamExt;
|
702
|
-
use http_body_util::StreamBody;
|
703
|
-
use hyper::body::Frame;
|
704
690
|
use tokio_util::io::ReaderStream;
|
705
691
|
|
706
692
|
match File::open(&path).await {
|
707
693
|
Ok(file) => {
|
708
694
|
let path_clone = path.clone();
|
709
|
-
let stream = ReaderStream::with_capacity(file, 64 * 1024)
|
710
|
-
.
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
});
|
715
|
-
Some(BoxBody::new(StreamBody::new(stream)))
|
695
|
+
let stream = ReaderStream::with_capacity(file, 64 * 1024).map_err(move |e| {
|
696
|
+
warn!("Error streaming file {}: {}", path_clone.display(), e);
|
697
|
+
unreachable!("We handle IO errors above")
|
698
|
+
});
|
699
|
+
Some(HttpBody::stream(stream))
|
716
700
|
}
|
717
701
|
Err(e) => {
|
718
702
|
warn!(
|
@@ -749,7 +733,7 @@ impl StaticFileServer {
|
|
749
733
|
return Response::builder()
|
750
734
|
.status(StatusCode::RANGE_NOT_SATISFIABLE)
|
751
735
|
.header("Content-Range", format!("bytes */{}", content_length))
|
752
|
-
.body(
|
736
|
+
.body(HttpBody::empty())
|
753
737
|
.unwrap();
|
754
738
|
}
|
755
739
|
|
@@ -795,7 +779,7 @@ impl StaticFileServer {
|
|
795
779
|
builder = builder.header("Content-Range", range);
|
796
780
|
}
|
797
781
|
|
798
|
-
return builder.body(
|
782
|
+
return builder.body(HttpBody::empty()).unwrap();
|
799
783
|
}
|
800
784
|
|
801
785
|
// For GET requests, prepare the actual content
|
@@ -829,10 +813,7 @@ impl StaticFileServer {
|
|
829
813
|
}
|
830
814
|
}
|
831
815
|
|
832
|
-
fn serve_cached_content(
|
833
|
-
&self,
|
834
|
-
serve_cache_args: ServeCacheArgs,
|
835
|
-
) -> http::Response<BoxBody<Bytes, Infallible>> {
|
816
|
+
fn serve_cached_content(&self, serve_cache_args: ServeCacheArgs) -> HttpResponse {
|
836
817
|
let ServeCacheArgs(
|
837
818
|
cache_entry,
|
838
819
|
start,
|
@@ -855,7 +836,7 @@ impl StaticFileServer {
|
|
855
836
|
return Response::builder()
|
856
837
|
.status(StatusCode::RANGE_NOT_SATISFIABLE)
|
857
838
|
.header("Content-Range", format!("bytes */{}", content_length))
|
858
|
-
.body(
|
839
|
+
.body(HttpBody::empty())
|
859
840
|
.unwrap();
|
860
841
|
}
|
861
842
|
|
@@ -904,7 +885,7 @@ impl StaticFileServer {
|
|
904
885
|
builder = builder.header("Content-Range", range);
|
905
886
|
}
|
906
887
|
|
907
|
-
return builder.body(
|
888
|
+
return builder.body(HttpBody::empty()).unwrap();
|
908
889
|
}
|
909
890
|
|
910
891
|
if is_range_request {
|
@@ -920,7 +901,7 @@ impl StaticFileServer {
|
|
920
901
|
cache_entry.last_modified_http_date.clone(),
|
921
902
|
content_range,
|
922
903
|
&self.headers,
|
923
|
-
|
904
|
+
HttpBody::full(range_bytes),
|
924
905
|
)
|
925
906
|
} else {
|
926
907
|
// Return the full content
|
@@ -987,15 +968,15 @@ fn format_http_date_header(time: SystemTime) -> HeaderValue {
|
|
987
968
|
.unwrap()
|
988
969
|
}
|
989
970
|
|
990
|
-
fn build_ok_body(bytes: Arc<Bytes>) ->
|
991
|
-
|
971
|
+
fn build_ok_body(bytes: Arc<Bytes>) -> HttpBody {
|
972
|
+
HttpBody::full(bytes.as_ref().clone())
|
992
973
|
}
|
993
974
|
|
994
975
|
// Helper function to handle not modified responses
|
995
|
-
fn build_not_modified_response() ->
|
976
|
+
fn build_not_modified_response() -> HttpResponse {
|
996
977
|
Response::builder()
|
997
978
|
.status(StatusCode::NOT_MODIFIED)
|
998
|
-
.body(
|
979
|
+
.body(HttpBody::empty())
|
999
980
|
.unwrap()
|
1000
981
|
}
|
1001
982
|
|
@@ -1009,8 +990,8 @@ fn build_file_response(
|
|
1009
990
|
last_modified_http_date: HeaderValue,
|
1010
991
|
range_header: Option<String>,
|
1011
992
|
headers: &Option<HashMap<String, String>>,
|
1012
|
-
body:
|
1013
|
-
) ->
|
993
|
+
body: HttpBody,
|
994
|
+
) -> HttpResponse {
|
1014
995
|
let mut response = Response::new(body);
|
1015
996
|
|
1016
997
|
*response.status_mut() = status;
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
2
|
+
|
3
|
+
# dependencies
|
4
|
+
/node_modules
|
5
|
+
|
6
|
+
# next.js
|
7
|
+
/.next/
|
8
|
+
/out/
|
9
|
+
|
10
|
+
# production
|
11
|
+
/build
|
12
|
+
|
13
|
+
# debug
|
14
|
+
npm-debug.log*
|
15
|
+
yarn-debug.log*
|
16
|
+
yarn-error.log*
|
17
|
+
.pnpm-debug.log*
|
18
|
+
|
19
|
+
# env files
|
20
|
+
.env*
|
21
|
+
|
22
|
+
# vercel
|
23
|
+
.vercel
|
24
|
+
|
25
|
+
# typescript
|
26
|
+
*.tsbuildinfo
|
27
|
+
next-env.d.ts
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import { NextResponse } from "next/server"
|
2
|
+
|
3
|
+
// This would be your actual API endpoint to fetch benchmark data
|
4
|
+
export async function GET() {
|
5
|
+
try {
|
6
|
+
// In a real implementation, you would:
|
7
|
+
// 1. Scan the directory structure
|
8
|
+
// 2. Read and parse the JSON files
|
9
|
+
// 3. Return the aggregated data
|
10
|
+
|
11
|
+
// For demo purposes, we're returning a mock response
|
12
|
+
return NextResponse.json({
|
13
|
+
success: true,
|
14
|
+
data: [
|
15
|
+
// Your benchmark data would go here
|
16
|
+
],
|
17
|
+
})
|
18
|
+
} catch (error) {
|
19
|
+
console.error("Error fetching benchmark data:", error)
|
20
|
+
return NextResponse.json({ success: false, error: "Failed to fetch benchmark data" }, { status: 500 })
|
21
|
+
}
|
22
|
+
}
|
@@ -0,0 +1,94 @@
|
|
1
|
+
@tailwind base;
|
2
|
+
@tailwind components;
|
3
|
+
@tailwind utilities;
|
4
|
+
|
5
|
+
body {
|
6
|
+
font-family: Arial, Helvetica, sans-serif;
|
7
|
+
}
|
8
|
+
|
9
|
+
@layer utilities {
|
10
|
+
.text-balance {
|
11
|
+
text-wrap: balance;
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
@layer base {
|
16
|
+
:root {
|
17
|
+
--background: 0 0% 100%;
|
18
|
+
--foreground: 0 0% 3.9%;
|
19
|
+
--card: 0 0% 100%;
|
20
|
+
--card-foreground: 0 0% 3.9%;
|
21
|
+
--popover: 0 0% 100%;
|
22
|
+
--popover-foreground: 0 0% 3.9%;
|
23
|
+
--primary: 0 0% 9%;
|
24
|
+
--primary-foreground: 0 0% 98%;
|
25
|
+
--secondary: 0 0% 96.1%;
|
26
|
+
--secondary-foreground: 0 0% 9%;
|
27
|
+
--muted: 0 0% 96.1%;
|
28
|
+
--muted-foreground: 0 0% 45.1%;
|
29
|
+
--accent: 0 0% 96.1%;
|
30
|
+
--accent-foreground: 0 0% 9%;
|
31
|
+
--destructive: 0 84.2% 60.2%;
|
32
|
+
--destructive-foreground: 0 0% 98%;
|
33
|
+
--border: 0 0% 89.8%;
|
34
|
+
--input: 0 0% 89.8%;
|
35
|
+
--ring: 0 0% 3.9%;
|
36
|
+
--chart-1: 12 76% 61%;
|
37
|
+
--chart-2: 173 58% 39%;
|
38
|
+
--chart-3: 197 37% 24%;
|
39
|
+
--chart-4: 43 74% 66%;
|
40
|
+
--chart-5: 27 87% 67%;
|
41
|
+
--radius: 0.5rem;
|
42
|
+
--sidebar-background: 0 0% 98%;
|
43
|
+
--sidebar-foreground: 240 5.3% 26.1%;
|
44
|
+
--sidebar-primary: 240 5.9% 10%;
|
45
|
+
--sidebar-primary-foreground: 0 0% 98%;
|
46
|
+
--sidebar-accent: 240 4.8% 95.9%;
|
47
|
+
--sidebar-accent-foreground: 240 5.9% 10%;
|
48
|
+
--sidebar-border: 220 13% 91%;
|
49
|
+
--sidebar-ring: 217.2 91.2% 59.8%;
|
50
|
+
}
|
51
|
+
.dark {
|
52
|
+
--background: 0 0% 3.9%;
|
53
|
+
--foreground: 0 0% 98%;
|
54
|
+
--card: 0 0% 3.9%;
|
55
|
+
--card-foreground: 0 0% 98%;
|
56
|
+
--popover: 0 0% 3.9%;
|
57
|
+
--popover-foreground: 0 0% 98%;
|
58
|
+
--primary: 0 0% 98%;
|
59
|
+
--primary-foreground: 0 0% 9%;
|
60
|
+
--secondary: 0 0% 14.9%;
|
61
|
+
--secondary-foreground: 0 0% 98%;
|
62
|
+
--muted: 0 0% 14.9%;
|
63
|
+
--muted-foreground: 0 0% 63.9%;
|
64
|
+
--accent: 0 0% 14.9%;
|
65
|
+
--accent-foreground: 0 0% 98%;
|
66
|
+
--destructive: 0 62.8% 30.6%;
|
67
|
+
--destructive-foreground: 0 0% 98%;
|
68
|
+
--border: 0 0% 14.9%;
|
69
|
+
--input: 0 0% 14.9%;
|
70
|
+
--ring: 0 0% 83.1%;
|
71
|
+
--chart-1: 220 70% 50%;
|
72
|
+
--chart-2: 160 60% 45%;
|
73
|
+
--chart-3: 30 80% 55%;
|
74
|
+
--chart-4: 280 65% 60%;
|
75
|
+
--chart-5: 340 75% 55%;
|
76
|
+
--sidebar-background: 240 5.9% 10%;
|
77
|
+
--sidebar-foreground: 240 4.8% 95.9%;
|
78
|
+
--sidebar-primary: 224.3 76.3% 48%;
|
79
|
+
--sidebar-primary-foreground: 0 0% 100%;
|
80
|
+
--sidebar-accent: 240 3.7% 15.9%;
|
81
|
+
--sidebar-accent-foreground: 240 4.8% 95.9%;
|
82
|
+
--sidebar-border: 240 3.7% 15.9%;
|
83
|
+
--sidebar-ring: 217.2 91.2% 59.8%;
|
84
|
+
}
|
85
|
+
}
|
86
|
+
|
87
|
+
@layer base {
|
88
|
+
* {
|
89
|
+
@apply border-border;
|
90
|
+
}
|
91
|
+
body {
|
92
|
+
@apply bg-background text-foreground;
|
93
|
+
}
|
94
|
+
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
import type { Metadata } from 'next'
|
2
|
+
import './globals.css'
|
3
|
+
|
4
|
+
export const metadata: Metadata = {
|
5
|
+
title: 'v0 App',
|
6
|
+
description: 'Created with v0',
|
7
|
+
generator: 'v0.dev',
|
8
|
+
}
|
9
|
+
|
10
|
+
export default function RootLayout({
|
11
|
+
children,
|
12
|
+
}: Readonly<{
|
13
|
+
children: React.ReactNode
|
14
|
+
}>) {
|
15
|
+
return (
|
16
|
+
<html lang="en">
|
17
|
+
<body>{children}</body>
|
18
|
+
</html>
|
19
|
+
)
|
20
|
+
}
|
@@ -0,0 +1,252 @@
|
|
1
|
+
"use client"
|
2
|
+
|
3
|
+
import { useState, useEffect } from "react"
|
4
|
+
import { BenchmarkDashboard } from "@/components/benchmark-dashboard"
|
5
|
+
import { LoadingSpinner } from "@/components/ui/loading-spinner"
|
6
|
+
|
7
|
+
// Updated sample data with the new group hierarchy and longer version strings
|
8
|
+
const sampleHierarchicalData = [
|
9
|
+
{
|
10
|
+
cpu: "apple_m1_pro",
|
11
|
+
groups: [
|
12
|
+
{
|
13
|
+
group: "rack",
|
14
|
+
tests: [
|
15
|
+
{
|
16
|
+
test: "chunked",
|
17
|
+
servers: [
|
18
|
+
{
|
19
|
+
server: "agoo",
|
20
|
+
results: [
|
21
|
+
{
|
22
|
+
server: "agoo",
|
23
|
+
test_case: "chunked",
|
24
|
+
version: "Agoo v1.2.3 (Ruby 3.2.0) with experimental HTTP/2 support",
|
25
|
+
threads: 1,
|
26
|
+
workers: 1,
|
27
|
+
http2: false,
|
28
|
+
concurrency: 10,
|
29
|
+
rss_mb: 1.77,
|
30
|
+
results: {
|
31
|
+
successRate: 1.0,
|
32
|
+
total: 20000.0,
|
33
|
+
slowest: 5000000.0,
|
34
|
+
fastest: 200000.0,
|
35
|
+
average: 2500000.0,
|
36
|
+
requestsPerSec: 6500.0,
|
37
|
+
totalData: 0,
|
38
|
+
sizePerRequest: 0.0,
|
39
|
+
sizePerSec: 0.0,
|
40
|
+
errorDistribution: {},
|
41
|
+
p95_latency: 4.8,
|
42
|
+
},
|
43
|
+
timestamp: "2025-05-24T10:41:17Z",
|
44
|
+
},
|
45
|
+
{
|
46
|
+
server: "agoo",
|
47
|
+
test_case: "chunked",
|
48
|
+
version: "Agoo v1.2.3 (Ruby 3.2.0) with experimental HTTP/2 support",
|
49
|
+
threads: 1,
|
50
|
+
workers: 1,
|
51
|
+
http2: false,
|
52
|
+
concurrency: 50,
|
53
|
+
rss_mb: 1.77,
|
54
|
+
results: {
|
55
|
+
successRate: 0.95,
|
56
|
+
total: 28000.0,
|
57
|
+
slowest: 12000000.0,
|
58
|
+
fastest: 400000.0,
|
59
|
+
average: 6000000.0,
|
60
|
+
requestsPerSec: 9500.0,
|
61
|
+
totalData: 0,
|
62
|
+
sizePerRequest: 0.0,
|
63
|
+
sizePerSec: 0.0,
|
64
|
+
errorDistribution: {
|
65
|
+
timeout: 50,
|
66
|
+
},
|
67
|
+
p95_latency: 10.2,
|
68
|
+
},
|
69
|
+
timestamp: "2025-05-24T10:41:17Z",
|
70
|
+
},
|
71
|
+
],
|
72
|
+
},
|
73
|
+
{
|
74
|
+
server: "puma",
|
75
|
+
results: [
|
76
|
+
{
|
77
|
+
server: "puma",
|
78
|
+
test_case: "chunked",
|
79
|
+
version:
|
80
|
+
"Puma version 6.6.0+h2o version 2.3.0-DEV@87e2aa634 (Ruby 3.2.2) with HTTP/2 support enabled",
|
81
|
+
threads: 1,
|
82
|
+
workers: 1,
|
83
|
+
http2: true,
|
84
|
+
concurrency: 10,
|
85
|
+
results: {
|
86
|
+
successRate: 1.0,
|
87
|
+
total: 15000.0,
|
88
|
+
slowest: 4000000.0,
|
89
|
+
fastest: 200000.0,
|
90
|
+
average: 2000000.0,
|
91
|
+
requestsPerSec: 5000.0,
|
92
|
+
totalData: 150000000,
|
93
|
+
sizePerRequest: 10000.0,
|
94
|
+
sizePerSec: 50000000.0,
|
95
|
+
errorDistribution: {},
|
96
|
+
p95_latency: 3.8,
|
97
|
+
},
|
98
|
+
},
|
99
|
+
{
|
100
|
+
server: "puma",
|
101
|
+
test_case: "chunked",
|
102
|
+
version:
|
103
|
+
"Puma version 6.6.0+h2o version 2.3.0-DEV@87e2aa634 (Ruby 3.2.2) with HTTP/2 support enabled",
|
104
|
+
threads: 1,
|
105
|
+
workers: 1,
|
106
|
+
http2: true,
|
107
|
+
concurrency: 50,
|
108
|
+
results: {
|
109
|
+
successRate: 1.0,
|
110
|
+
total: 22000.0,
|
111
|
+
slowest: 12000000.0,
|
112
|
+
fastest: 400000.0,
|
113
|
+
average: 6000000.0,
|
114
|
+
requestsPerSec: 7500.0,
|
115
|
+
totalData: 220000000,
|
116
|
+
sizePerRequest: 10000.0,
|
117
|
+
sizePerSec: 75000000.0,
|
118
|
+
errorDistribution: {},
|
119
|
+
p95_latency: 11.2,
|
120
|
+
},
|
121
|
+
},
|
122
|
+
],
|
123
|
+
},
|
124
|
+
],
|
125
|
+
},
|
126
|
+
{
|
127
|
+
test: "io_party",
|
128
|
+
servers: [
|
129
|
+
{
|
130
|
+
server: "itsi",
|
131
|
+
results: [
|
132
|
+
{
|
133
|
+
server: "itsi",
|
134
|
+
test_case: "io_party",
|
135
|
+
version:
|
136
|
+
"ITSI v0.9.1-beta.3 (Experimental) with advanced IO processing and HTTP/2 multiplexing support",
|
137
|
+
threads: 1,
|
138
|
+
workers: 1,
|
139
|
+
http2: true,
|
140
|
+
concurrency: 10,
|
141
|
+
rss_mb: 64.11,
|
142
|
+
results: {
|
143
|
+
successRate: 1.0,
|
144
|
+
total: 48591.0,
|
145
|
+
slowest: 2850375.0,
|
146
|
+
fastest: 116292.0,
|
147
|
+
average: 486975.0,
|
148
|
+
requestsPerSec: 16196.49,
|
149
|
+
totalData: 0,
|
150
|
+
sizePerRequest: 0.0,
|
151
|
+
sizePerSec: 0.0,
|
152
|
+
errorDistribution: {},
|
153
|
+
p95_latency: 0.977999,
|
154
|
+
},
|
155
|
+
timestamp: "2025-05-15T03:43:50Z",
|
156
|
+
},
|
157
|
+
],
|
158
|
+
},
|
159
|
+
],
|
160
|
+
},
|
161
|
+
],
|
162
|
+
},
|
163
|
+
{
|
164
|
+
group: "sinatra",
|
165
|
+
tests: [
|
166
|
+
{
|
167
|
+
test: "hello_world",
|
168
|
+
servers: [
|
169
|
+
{
|
170
|
+
server: "falcon",
|
171
|
+
results: [
|
172
|
+
{
|
173
|
+
server: "falcon",
|
174
|
+
test_case: "hello_world",
|
175
|
+
version:
|
176
|
+
"Falcon v0.51.1 (Ruby 3.3.0-preview1) with HTTP/2 and WebSocket support, running on Async::HTTP::Protocol::HTTP2 implementation",
|
177
|
+
threads: 2,
|
178
|
+
workers: 2,
|
179
|
+
http2: true,
|
180
|
+
concurrency: 10,
|
181
|
+
results: {
|
182
|
+
successRate: 1.0,
|
183
|
+
total: 30000.0,
|
184
|
+
slowest: 10000000.0,
|
185
|
+
fastest: 400000.0,
|
186
|
+
average: 5000000.0,
|
187
|
+
requestsPerSec: 10000.0,
|
188
|
+
totalData: 0,
|
189
|
+
sizePerRequest: 0.0,
|
190
|
+
sizePerSec: 0.0,
|
191
|
+
errorDistribution: {},
|
192
|
+
p95_latency: 8.5,
|
193
|
+
},
|
194
|
+
},
|
195
|
+
],
|
196
|
+
},
|
197
|
+
],
|
198
|
+
},
|
199
|
+
],
|
200
|
+
},
|
201
|
+
],
|
202
|
+
},
|
203
|
+
]
|
204
|
+
|
205
|
+
export default function Home() {
|
206
|
+
const [isLoading, setIsLoading] = useState(true)
|
207
|
+
const [benchmarkData, setBenchmarkData] = useState([])
|
208
|
+
const [error, setError] = useState<string | null>(null)
|
209
|
+
|
210
|
+
useEffect(() => {
|
211
|
+
const fetchData = async () => {
|
212
|
+
try {
|
213
|
+
// In a real app, this would fetch from your API endpoint
|
214
|
+
// For demo purposes, we're using the sample hierarchical data
|
215
|
+
setBenchmarkData(sampleHierarchicalData)
|
216
|
+
setIsLoading(false)
|
217
|
+
} catch (err) {
|
218
|
+
console.error("Error fetching benchmark data:", err)
|
219
|
+
setError("Failed to load benchmark data. Please try again.")
|
220
|
+
setIsLoading(false)
|
221
|
+
}
|
222
|
+
}
|
223
|
+
|
224
|
+
fetchData()
|
225
|
+
}, [])
|
226
|
+
|
227
|
+
if (isLoading) {
|
228
|
+
return (
|
229
|
+
<div className="flex h-screen items-center justify-center">
|
230
|
+
<LoadingSpinner />
|
231
|
+
<span className="ml-2">Loading benchmark data...</span>
|
232
|
+
</div>
|
233
|
+
)
|
234
|
+
}
|
235
|
+
|
236
|
+
if (error) {
|
237
|
+
return (
|
238
|
+
<div className="flex h-screen items-center justify-center">
|
239
|
+
<div className="text-center">
|
240
|
+
<h2 className="text-2xl font-bold text-red-600">Error</h2>
|
241
|
+
<p className="mt-2">{error}</p>
|
242
|
+
</div>
|
243
|
+
</div>
|
244
|
+
)
|
245
|
+
}
|
246
|
+
|
247
|
+
return (
|
248
|
+
<main className="container mx-auto p-2">
|
249
|
+
<BenchmarkDashboard data={benchmarkData} />
|
250
|
+
</main>
|
251
|
+
)
|
252
|
+
}
|