Files
oplevel/cors_proxy.ml

218 lines
8.2 KiB
OCaml

module Format = Stdlib.Format
module Arg = Stdlib.Arg
open Lwt.Infix
open Httpaf
module Server = struct
include Httpaf_lwt_unix.Server
let error_handler ?request:_ error start_response =
let response_body = start_response Headers.empty in
(match error with
| `Exn exn ->
Body.write_string response_body (Base.Exn.to_string exn);
Body.write_string response_body "\n"
| #Status.standard as error ->
Body.write_string response_body
(Status.default_reason_phrase error));
Body.close_writer response_body
end
let proxy_host = "gitea.departmentofinter.net"
let file_response_headers =
[
("accept-ranges", "bytes");
("access-control-allow-origin", "*");
( "access-control-allow-headers",
"Origin, X-Requested-With, Content-Type, Accept, Range" );
("cache-control", "no-cache, no-store, must-revalidate");
("etag", "W/\"6861266-5562-2024-02-11T19:05:04.733Z\"");
("content-type", "text/html; charset=UTF-8");
("connection", "close");
]
let proxy_handler reqd : unit =
match Reqd.request reqd with
| Request.{ meth; headers; target; _ } ->
Lwt.async (fun () ->
let filename = Fmt.str "./%s" target in
if
Sys.file_exists
filename (* && Sys.is_regular_file filename *)
then (
let file =
Lwt_io.open_file ~flags:[ O_RDONLY ] ~mode:Lwt_io.input
filename
in
let headers = Headers.of_list file_response_headers in
Lwt_io.file_length filename >>= fun file_len ->
let headers =
Headers.add_list headers
[ ("content-length", Int64.to_string file_len) ]
in
Format.printf ">>> Response with File:\nHeaders:\n%s\n %!"
(Headers.to_string headers);
let response = Response.create ~headers `OK in
let response_body =
Reqd.respond_with_streaming reqd response
in
file >>= fun file ->
let rec reader () =
if
Int64.compare
(Int64.sub file_len (Lwt_io.position file))
Int64.zero
> 0
then (
Lwt_io.read file >>= fun s ->
Format.printf ".%i\n%!" (String.length s);
Body.write_string response_body s;
reader ())
else Lwt.return_unit >>= fun () -> Lwt.return_unit
in
reader () >>= fun () ->
Body.close_writer response_body;
Lwt_io.close file >>= fun () ->
Format.printf "done%!";
Lwt.return_unit)
else
let uri = Fmt.str "https://%s%s" proxy_host target in
let proxy_finished, proxy_notify_finished = Lwt.wait () in
let proxy_response_handler _
(proxy_response_body_acc : Buffer.t)
proxy_response_body =
Format.printf ">>> proxy_response_body chunk: %i\n%!"
(String.length proxy_response_body);
Buffer.add_string proxy_response_body_acc
proxy_response_body;
Lwt.return proxy_response_body_acc
in
let request_headers =
Headers.replace headers "Host" proxy_host
in
let request_headers =
Headers.replace request_headers "origin"
(Fmt.str "https://%s" proxy_host)
in
let request_headers =
Headers.replace request_headers "content-type"
"application/x-git-upload-pack-request"
in
let proxy_request_headers =
Headers.of_list
[
("Accept-Language", "en-US, *;q=0.9");
("Accept", "application/x-git-upload-pack-result");
( "Content-Type",
"application/x-git-receive-upload-request" );
("Accept-Encoding", "deflate, gzip, br, zstd");
("User-Agent", "git/2.43.0");
( "Authorization",
"Basic \
Y3FjOmQ5YzJiNDkxZTcwZTMxYTc2MGNlNzBiYzQzMTAzNmM5MTMyNWY2ODM="
);
("Host", "gitea.departmentofinter.net");
( "Content-Length",
Option.fold ~some:Fun.id ~none:"0"
(Headers.get headers "content-length") );
]
in
let request_body = Reqd.request_body reqd in
let request_body_buffer =
Bigstringaf.create
(Option.fold ~some:int_of_string ~none:4096
(Headers.get headers "content-length"))
in
let response_body_buffer = Buffer.create 0 in
let rec on_read buffer ~off ~len =
Bigstringaf.blit buffer ~src_off:off request_body_buffer
~dst_off:off ~len;
Body.schedule_read request_body ~on_eof ~on_read
and on_eof () = Body.close_reader request_body in
Body.schedule_read request_body ~on_eof ~on_read;
Format.printf
">>> Proxy Request: Target:%s Headers:\n%s\n%!" target
(String.trim (Headers.to_string request_headers));
Format.printf ">>> Proxy Request Body length: %i\n%!"
(Bigstringaf.length request_body_buffer);
Http_lwt_client.request
?config:(Some (`HTTP_1_1 Httpaf.Config.default))
~meth
~headers:(Headers.to_list request_headers)
?body:(Some (Bigstringaf.to_string request_body_buffer))
~follow_redirect:false uri proxy_response_handler
response_body_buffer
>>= function
| Ok
( { version; status; reason; headers },
proxy_response_body_acc ) ->
Format.printf
">>> Response Ok: Code: %i Reason: %s Headers:\n\
%s\n\
%!"
(H2.Status.to_code status)
reason
(String.trim (H2.Headers.to_string headers));
let headers =
Headers.of_list (H2.Headers.to_list headers)
in
let status =
Status.of_code (H2.Status.to_code status)
in
let response =
let content_type =
match Headers.get headers "content-type" with
| None -> "application/octet-stream"
| Some x -> x
in
Response.create ~reason ~version
~headers:
(Headers.replace headers "content-type"
content_type)
status
in
let response_body =
Reqd.respond_with_streaming reqd response
in
Body.write_string response_body ~off:0
~len:(Buffer.length proxy_response_body_acc)
(Buffer.contents proxy_response_body_acc);
Body.close_writer response_body;
Lwt.wakeup_later proxy_notify_finished ();
Lwt.return_unit
| Error (`Msg msg) ->
Format.printf "Request failed with %s%!" msg;
Lwt.return_unit >>= fun () -> proxy_finished)
(* | _ ->
let headers = Headers.of_list [ ("connection", "close") ] in
Reqd.respond_with_string reqd
(Response.create ~headers `Method_not_allowed)
"" *)
let request_handler (_ : Unix.sockaddr) = proxy_handler
let error_handler (_ : Unix.sockaddr) = Server.error_handler
let main port =
let listen_address = Unix.(ADDR_INET (inet_addr_loopback, port)) in
Lwt.async (fun () ->
Lwt_io.establish_server_with_client_socket listen_address
(Server.create_connection_handler ~request_handler
~error_handler)
>|= fun _server ->
Stdio.printf
"Listening on localhost:%i and proxying requests via \
https://%s.\n\
%!"
port proxy_host);
let forever, _ = Lwt.wait () in
Lwt_main.run forever
let () =
let port = ref 8080 in
main !port