1use crate::gateway::{
217 RiGateway, RiGatewayConfig, RiRouter, RiRoute, RiRouteHandler,
218 RiRateLimiter, RiRateLimitConfig,
219 RiCircuitBreaker, RiCircuitBreakerConfig, RiCircuitBreakerState,
220 RiLoadBalancer, RiLoadBalancerStrategy, RiBackendServer,
221 RiGatewayRequest, RiGatewayResponse,
222};
223use crate::core::RiResult;
224use std::ffi::{c_char, c_int};
225use std::future::Future;
226use std::pin::Pin;
227use std::sync::Arc;
228
229c_wrapper!(CRiGateway, RiGateway);
230c_wrapper!(CRiGatewayConfig, RiGatewayConfig);
231c_wrapper!(CRiRouter, RiRouter);
232c_wrapper!(CRiRateLimiter, RiRateLimiter);
233c_wrapper!(CRiRateLimitConfig, RiRateLimitConfig);
234c_wrapper!(CRiCircuitBreaker, RiCircuitBreaker);
235c_wrapper!(CRiCircuitBreakerConfig, RiCircuitBreakerConfig);
236c_wrapper!(CRiLoadBalancer, RiLoadBalancer);
237
238c_constructor!(ri_gateway_config_new, CRiGatewayConfig, RiGatewayConfig, RiGatewayConfig::default());
239c_destructor!(ri_gateway_config_free, CRiGatewayConfig);
240
241#[no_mangle]
242pub extern "C" fn ri_router_new() -> *mut CRiRouter {
243 let router = RiRouter::new();
244 let ptr = Box::into_raw(Box::new(CRiRouter::new(router)));
245 crate::c::register_ptr(ptr as usize);
246 ptr
247}
248
249#[no_mangle]
250pub extern "C" fn ri_router_free(router: *mut CRiRouter) {
251 if router.is_null() {
252 return;
253 }
254
255 if !crate::c::unregister_ptr(router as usize) {
256 log::warn!(
257 "[Ri.C] Attempted to free unregistered or already freed router: {:?}",
258 router
259 );
260 return;
261 }
262
263 unsafe {
264 let _ = Box::from_raw(router);
265 }
266}
267
268#[no_mangle]
269pub extern "C" fn ri_router_add_route(
270 router: *mut CRiRouter,
271 method: *const c_char,
272 path: *const c_char,
273) -> c_int {
274 if router.is_null() || method.is_null() || path.is_null() {
275 return -1;
276 }
277
278 unsafe {
279 let method_str = match std::ffi::CStr::from_ptr(method).to_str() {
280 Ok(s) => s,
281 Err(_) => return -2,
282 };
283
284 let path_str = match std::ffi::CStr::from_ptr(path).to_str() {
285 Ok(s) => s,
286 Err(_) => return -3,
287 };
288
289 let handler: RiRouteHandler = Arc::new(|_req: RiGatewayRequest| {
290 Box::pin(async move {
291 Ok(crate::gateway::RiGatewayResponse::new(
292 200,
293 b"OK".to_vec(),
294 String::new(),
295 ))
296 }) as Pin<Box<dyn Future<Output = RiResult<RiGatewayResponse>> + Send>>
297 });
298
299 let route = RiRoute::new(method_str.to_string(), path_str.to_string(), handler);
300 (*router).inner.add_route(route);
301 0
302 }
303}
304
305#[no_mangle]
306pub extern "C" fn ri_router_clear_routes(router: *mut CRiRouter) {
307 if router.is_null() {
308 return;
309 }
310 unsafe {
311 (*router).inner.clear_routes();
312 }
313}
314
315#[no_mangle]
316pub extern "C" fn ri_router_route_count(router: *mut CRiRouter) -> usize {
317 if router.is_null() {
318 return 0;
319 }
320 unsafe {
321 (*router).inner.route_count()
322 }
323}
324
325#[no_mangle]
326pub extern "C" fn ri_rate_limiter_new(
327 requests_per_second: u32,
328 burst_size: u32,
329 window_seconds: u64,
330) -> *mut CRiRateLimiter {
331 let config = RiRateLimitConfig {
332 requests_per_second,
333 burst_size,
334 window_seconds,
335 max_keys: 10000,
336 };
337 let limiter = RiRateLimiter::new(config);
338 Box::into_raw(Box::new(CRiRateLimiter::new(limiter)))
339}
340
341#[no_mangle]
342pub extern "C" fn ri_rate_limiter_free(limiter: *mut CRiRateLimiter) {
343 if !limiter.is_null() {
344 unsafe {
345 let _ = Box::from_raw(limiter);
346 }
347 }
348}
349
350#[no_mangle]
351pub extern "C" fn ri_rate_limiter_check(
352 limiter: *mut CRiRateLimiter,
353 key: *const c_char,
354) -> c_int {
355 if limiter.is_null() || key.is_null() {
356 return -1;
357 }
358
359 unsafe {
360 let key_str = match std::ffi::CStr::from_ptr(key).to_str() {
361 Ok(s) => s,
362 Err(_) => return -2,
363 };
364
365 if (*limiter).inner.check_rate_limit(key_str, 1) {
366 0
367 } else {
368 1
369 }
370 }
371}
372
373#[no_mangle]
374pub extern "C" fn ri_rate_limiter_get_remaining(
375 limiter: *mut CRiRateLimiter,
376 key: *const c_char,
377) -> f64 {
378 if limiter.is_null() || key.is_null() {
379 return -1.0;
380 }
381
382 unsafe {
383 let key_str = match std::ffi::CStr::from_ptr(key).to_str() {
384 Ok(s) => s,
385 Err(_) => return -2.0,
386 };
387
388 (*limiter).inner.get_remaining(key_str).unwrap_or(0.0)
389 }
390}
391
392#[no_mangle]
393pub extern "C" fn ri_rate_limiter_reset_bucket(
394 limiter: *mut CRiRateLimiter,
395 key: *const c_char,
396) {
397 if limiter.is_null() || key.is_null() {
398 return;
399 }
400
401 unsafe {
402 let key_str = match std::ffi::CStr::from_ptr(key).to_str() {
403 Ok(s) => s,
404 Err(_) => return,
405 };
406
407 (*limiter).inner.reset_bucket(key_str);
408 }
409}
410
411#[no_mangle]
412pub extern "C" fn ri_rate_limiter_clear_all(limiter: *mut CRiRateLimiter) {
413 if limiter.is_null() {
414 return;
415 }
416 unsafe {
417 (*limiter).inner.clear_all_buckets();
418 }
419}
420
421#[repr(C)]
422pub struct CRiRateLimitStats {
423 pub current_tokens: usize,
424 pub total_requests: usize,
425}
426
427#[no_mangle]
428pub extern "C" fn ri_rate_limiter_get_stats(
429 limiter: *mut CRiRateLimiter,
430 key: *const c_char,
431 out_stats: *mut CRiRateLimitStats,
432) -> c_int {
433 if limiter.is_null() || key.is_null() || out_stats.is_null() {
434 return -1;
435 }
436
437 unsafe {
438 let key_str = match std::ffi::CStr::from_ptr(key).to_str() {
439 Ok(s) => s,
440 Err(_) => return -2,
441 };
442
443 if let Some(stats) = (*limiter).inner.get_stats(key_str) {
444 *out_stats = CRiRateLimitStats {
445 current_tokens: stats.current_tokens,
446 total_requests: stats.total_requests,
447 };
448 0
449 } else {
450 -3
451 }
452 }
453}
454
455#[no_mangle]
456pub extern "C" fn ri_circuit_breaker_new(
457 failure_threshold: u32,
458 success_threshold: u32,
459 timeout_seconds: u64,
460) -> *mut CRiCircuitBreaker {
461 let config = RiCircuitBreakerConfig {
462 failure_threshold,
463 success_threshold,
464 timeout_seconds,
465 monitoring_period_seconds: 30,
466 };
467 let cb = RiCircuitBreaker::new(config);
468 Box::into_raw(Box::new(CRiCircuitBreaker::new(cb)))
469}
470
471#[no_mangle]
472pub extern "C" fn ri_circuit_breaker_free(cb: *mut CRiCircuitBreaker) {
473 if !cb.is_null() {
474 unsafe {
475 let _ = Box::from_raw(cb);
476 }
477 }
478}
479
480#[no_mangle]
481pub extern "C" fn ri_circuit_breaker_allow_request(cb: *mut CRiCircuitBreaker) -> c_int {
482 if cb.is_null() {
483 return 0;
484 }
485 unsafe {
486 if (*cb).inner.allow_request() { 1 } else { 0 }
487 }
488}
489
490#[no_mangle]
491pub extern "C" fn ri_circuit_breaker_record_success(cb: *mut CRiCircuitBreaker) {
492 if cb.is_null() {
493 return;
494 }
495 unsafe {
496 (*cb).inner.record_success();
497 }
498}
499
500#[no_mangle]
501pub extern "C" fn ri_circuit_breaker_record_failure(cb: *mut CRiCircuitBreaker) {
502 if cb.is_null() {
503 return;
504 }
505 unsafe {
506 (*cb).inner.record_failure();
507 }
508}
509
510pub const RI_CB_STATE_CLOSED: c_int = 0;
511pub const RI_CB_STATE_OPEN: c_int = 1;
512pub const RI_CB_STATE_HALF_OPEN: c_int = 2;
513
514#[no_mangle]
515pub extern "C" fn ri_circuit_breaker_get_state(cb: *mut CRiCircuitBreaker) -> c_int {
516 if cb.is_null() {
517 return RI_CB_STATE_CLOSED;
518 }
519 unsafe {
520 match (*cb).inner.get_state() {
521 RiCircuitBreakerState::Closed => RI_CB_STATE_CLOSED,
522 RiCircuitBreakerState::Open => RI_CB_STATE_OPEN,
523 RiCircuitBreakerState::HalfOpen => RI_CB_STATE_HALF_OPEN,
524 }
525 }
526}
527
528#[repr(C)]
529pub struct CRiCircuitBreakerMetrics {
530 pub state: c_int,
531 pub failure_count: usize,
532 pub success_count: usize,
533 pub consecutive_failures: usize,
534 pub consecutive_successes: usize,
535}
536
537#[no_mangle]
538pub extern "C" fn ri_circuit_breaker_get_stats(
539 cb: *mut CRiCircuitBreaker,
540 out_stats: *mut CRiCircuitBreakerMetrics,
541) -> c_int {
542 if cb.is_null() || out_stats.is_null() {
543 return -1;
544 }
545
546 unsafe {
547 let stats = (*cb).inner.get_stats();
548 let state = match stats.state.as_str() {
549 "Closed" => RI_CB_STATE_CLOSED,
550 "Open" => RI_CB_STATE_OPEN,
551 "HalfOpen" => RI_CB_STATE_HALF_OPEN,
552 _ => RI_CB_STATE_CLOSED,
553 };
554
555 *out_stats = CRiCircuitBreakerMetrics {
556 state,
557 failure_count: stats.failure_count,
558 success_count: stats.success_count,
559 consecutive_failures: stats.consecutive_failures,
560 consecutive_successes: stats.consecutive_successes,
561 };
562 0
563 }
564}
565
566#[no_mangle]
567pub extern "C" fn ri_circuit_breaker_reset(cb: *mut CRiCircuitBreaker) {
568 if cb.is_null() {
569 return;
570 }
571 unsafe {
572 (*cb).inner.reset();
573 }
574}
575
576#[no_mangle]
577pub extern "C" fn ri_circuit_breaker_force_open(cb: *mut CRiCircuitBreaker) {
578 if cb.is_null() {
579 return;
580 }
581 unsafe {
582 (*cb).inner.force_open();
583 }
584}
585
586#[no_mangle]
587pub extern "C" fn ri_circuit_breaker_force_close(cb: *mut CRiCircuitBreaker) {
588 if cb.is_null() {
589 return;
590 }
591 unsafe {
592 (*cb).inner.force_close();
593 }
594}
595
596pub const RI_LB_STRATEGY_ROUND_ROBIN: c_int = 0;
597pub const RI_LB_STRATEGY_WEIGHTED_ROUND_ROBIN: c_int = 1;
598pub const RI_LB_STRATEGY_LEAST_CONNECTIONS: c_int = 2;
599pub const RI_LB_STRATEGY_RANDOM: c_int = 3;
600pub const RI_LB_STRATEGY_IP_HASH: c_int = 4;
601pub const RI_LB_STRATEGY_LEAST_RESPONSE_TIME: c_int = 5;
602pub const RI_LB_STRATEGY_CONSISTENT_HASH: c_int = 6;
603
604fn strategy_from_c_int(strategy: c_int) -> RiLoadBalancerStrategy {
605 match strategy {
606 RI_LB_STRATEGY_ROUND_ROBIN => RiLoadBalancerStrategy::RoundRobin,
607 RI_LB_STRATEGY_WEIGHTED_ROUND_ROBIN => RiLoadBalancerStrategy::WeightedRoundRobin,
608 RI_LB_STRATEGY_LEAST_CONNECTIONS => RiLoadBalancerStrategy::LeastConnections,
609 RI_LB_STRATEGY_RANDOM => RiLoadBalancerStrategy::Random,
610 RI_LB_STRATEGY_IP_HASH => RiLoadBalancerStrategy::IpHash,
611 RI_LB_STRATEGY_LEAST_RESPONSE_TIME => RiLoadBalancerStrategy::LeastResponseTime,
612 RI_LB_STRATEGY_CONSISTENT_HASH => RiLoadBalancerStrategy::ConsistentHash,
613 _ => RiLoadBalancerStrategy::RoundRobin,
614 }
615}
616
617#[no_mangle]
618pub extern "C" fn ri_load_balancer_new(strategy: c_int) -> *mut CRiLoadBalancer {
619 let lb = RiLoadBalancer::new(strategy_from_c_int(strategy));
620 Box::into_raw(Box::new(CRiLoadBalancer::new(lb)))
621}
622
623#[no_mangle]
624pub extern "C" fn ri_load_balancer_free(lb: *mut CRiLoadBalancer) {
625 if !lb.is_null() {
626 unsafe {
627 let _ = Box::from_raw(lb);
628 }
629 }
630}
631
632#[no_mangle]
633pub extern "C" fn ri_load_balancer_add_server(
634 lb: *mut CRiLoadBalancer,
635 id: *const c_char,
636 url: *const c_char,
637 weight: u32,
638 max_connections: usize,
639) -> c_int {
640 if lb.is_null() || id.is_null() || url.is_null() {
641 return -1;
642 }
643
644 unsafe {
645 let id_str = match std::ffi::CStr::from_ptr(id).to_str() {
646 Ok(s) => s,
647 Err(_) => return -2,
648 };
649
650 let url_str = match std::ffi::CStr::from_ptr(url).to_str() {
651 Ok(s) => s,
652 Err(_) => return -3,
653 };
654
655 let server = RiBackendServer {
656 id: id_str.to_string(),
657 url: url_str.to_string(),
658 weight,
659 max_connections,
660 health_check_path: "/health".to_string(),
661 is_healthy: true,
662 };
663
664 let rt = match tokio::runtime::Runtime::new() {
665 Ok(r) => r,
666 Err(_) => return -4,
667 };
668
669 rt.block_on(async {
670 let _ = (*lb).inner.add_server(server).await;
671 });
672
673 0
674 }
675}
676
677#[no_mangle]
678pub extern "C" fn ri_load_balancer_remove_server(
679 lb: *mut CRiLoadBalancer,
680 id: *const c_char,
681) -> c_int {
682 if lb.is_null() || id.is_null() {
683 return -1;
684 }
685
686 unsafe {
687 let id_str = match std::ffi::CStr::from_ptr(id).to_str() {
688 Ok(s) => s,
689 Err(_) => return -2,
690 };
691
692 let rt = match tokio::runtime::Runtime::new() {
693 Ok(r) => r,
694 Err(_) => return -3,
695 };
696
697 let removed = rt.block_on(async {
698 (*lb).inner.remove_server(id_str).await
699 });
700
701 if removed { 0 } else { 1 }
702 }
703}
704
705#[repr(C)]
706pub struct CRiBackendServer {
707 pub id: *mut c_char,
708 pub url: *mut c_char,
709 pub weight: u32,
710 pub max_connections: usize,
711 pub is_healthy: c_int,
712}
713
714#[no_mangle]
715pub extern "C" fn ri_load_balancer_select_server(
716 lb: *mut CRiLoadBalancer,
717 client_ip: *const c_char,
718 out_server: *mut CRiBackendServer,
719) -> c_int {
720 if lb.is_null() || out_server.is_null() {
721 return -1;
722 }
723
724 unsafe {
725 let client_ip_str = if client_ip.is_null() {
726 None
727 } else {
728 match std::ffi::CStr::from_ptr(client_ip).to_str() {
729 Ok(s) => Some(s),
730 Err(_) => None,
731 }
732 };
733
734 let rt = match tokio::runtime::Runtime::new() {
735 Ok(r) => r,
736 Err(_) => return -2,
737 };
738
739 let result = rt.block_on(async {
740 (*lb).inner.select_server(client_ip_str).await
741 });
742
743 match result {
744 Ok(server) => {
745 let id = match std::ffi::CString::new(server.id.clone()) {
746 Ok(s) => s.into_raw(),
747 Err(_) => return -3,
748 };
749
750 let url = match std::ffi::CString::new(server.url.clone()) {
751 Ok(s) => s.into_raw(),
752 Err(_) => {
753 let _ = std::ffi::CString::from_raw(id);
754 return -4;
755 }
756 };
757
758 *out_server = CRiBackendServer {
759 id,
760 url,
761 weight: server.weight,
762 max_connections: server.max_connections,
763 is_healthy: if server.is_healthy { 1 } else { 0 },
764 };
765 0
766 }
767 Err(_) => -5,
768 }
769 }
770}
771
772#[no_mangle]
773pub extern "C" fn ri_backend_server_free(server: *mut CRiBackendServer) {
774 if server.is_null() {
775 return;
776 }
777
778 unsafe {
779 let server = Box::from_raw(server);
780 if !server.id.is_null() {
781 let _ = std::ffi::CString::from_raw(server.id);
782 }
783 if !server.url.is_null() {
784 let _ = std::ffi::CString::from_raw(server.url);
785 }
786 }
787}
788
789#[no_mangle]
790pub extern "C" fn ri_load_balancer_release_server(
791 lb: *mut CRiLoadBalancer,
792 server_id: *const c_char,
793) {
794 if lb.is_null() || server_id.is_null() {
795 return;
796 }
797
798 unsafe {
799 let id_str = match std::ffi::CStr::from_ptr(server_id).to_str() {
800 Ok(s) => s,
801 Err(_) => return,
802 };
803
804 let rt = match tokio::runtime::Runtime::new() {
805 Ok(r) => r,
806 Err(_) => return,
807 };
808
809 rt.block_on(async {
810 (*lb).inner.release_server(id_str).await;
811 });
812 }
813}
814
815#[no_mangle]
816pub extern "C" fn ri_load_balancer_mark_healthy(
817 lb: *mut CRiLoadBalancer,
818 server_id: *const c_char,
819 healthy: c_int,
820) {
821 if lb.is_null() || server_id.is_null() {
822 return;
823 }
824
825 unsafe {
826 let id_str = match std::ffi::CStr::from_ptr(server_id).to_str() {
827 Ok(s) => s,
828 Err(_) => return,
829 };
830
831 let rt = match tokio::runtime::Runtime::new() {
832 Ok(r) => r,
833 Err(_) => return,
834 };
835
836 rt.block_on(async {
837 (*lb).inner.mark_server_healthy(id_str, healthy != 0).await;
838 });
839 }
840}
841
842#[repr(C)]
843pub struct CRiLoadBalancerServerStats {
844 pub active_connections: usize,
845 pub total_requests: usize,
846 pub failed_requests: usize,
847 pub response_time_ms: usize,
848}
849
850#[no_mangle]
851pub extern "C" fn ri_load_balancer_get_server_stats(
852 lb: *mut CRiLoadBalancer,
853 server_id: *const c_char,
854 out_stats: *mut CRiLoadBalancerServerStats,
855) -> c_int {
856 if lb.is_null() || server_id.is_null() || out_stats.is_null() {
857 return -1;
858 }
859
860 unsafe {
861 let id_str = match std::ffi::CStr::from_ptr(server_id).to_str() {
862 Ok(s) => s,
863 Err(_) => return -2,
864 };
865
866 let rt = match tokio::runtime::Runtime::new() {
867 Ok(r) => r,
868 Err(_) => return -3,
869 };
870
871 let result = rt.block_on(async {
872 (*lb).inner.get_server_stats(id_str).await
873 });
874
875 match result {
876 Some(stats) => {
877 *out_stats = CRiLoadBalancerServerStats {
878 active_connections: stats.active_connections,
879 total_requests: stats.total_requests,
880 failed_requests: stats.failed_requests,
881 response_time_ms: stats.response_time_ms,
882 };
883 0
884 }
885 None => -4,
886 }
887 }
888}
889
890#[no_mangle]
891pub extern "C" fn ri_load_balancer_get_server_count(lb: *mut CRiLoadBalancer) -> usize {
892 if lb.is_null() {
893 return 0;
894 }
895
896 let rt = match tokio::runtime::Runtime::new() {
897 Ok(r) => r,
898 Err(_) => return 0,
899 };
900
901 unsafe {
902 rt.block_on(async {
903 (*lb).inner.get_server_count().await
904 })
905 }
906}
907
908#[no_mangle]
909pub extern "C" fn ri_load_balancer_get_healthy_count(lb: *mut CRiLoadBalancer) -> usize {
910 if lb.is_null() {
911 return 0;
912 }
913
914 let rt = match tokio::runtime::Runtime::new() {
915 Ok(r) => r,
916 Err(_) => return 0,
917 };
918
919 unsafe {
920 rt.block_on(async {
921 (*lb).inner.get_healthy_server_count().await
922 })
923 }
924}