{"id":20479164,"url":"https://github.com/kkazuo/horned_worm","last_synced_at":"2025-10-09T07:37:12.188Z","repository":{"id":43427668,"uuid":"106727887","full_name":"kkazuo/horned_worm","owner":"kkazuo","description":"An easy functional Web app micro framework.","archived":false,"fork":false,"pushed_at":"2020-11-05T14:21:20.000Z","size":33,"stargazers_count":24,"open_issues_count":0,"forks_count":0,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-13T13:38:47.749Z","etag":null,"topics":["async","horned-worm","http","ocaml","webservice"],"latest_commit_sha":null,"homepage":"","language":"OCaml","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kkazuo.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.MIT","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-10-12T18:02:55.000Z","updated_at":"2024-02-22T15:20:15.000Z","dependencies_parsed_at":"2022-07-07T16:18:27.419Z","dependency_job_id":null,"html_url":"https://github.com/kkazuo/horned_worm","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/kkazuo/horned_worm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kkazuo%2Fhorned_worm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kkazuo%2Fhorned_worm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kkazuo%2Fhorned_worm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kkazuo%2Fhorned_worm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kkazuo","download_url":"https://codeload.github.com/kkazuo/horned_worm/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kkazuo%2Fhorned_worm/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279000960,"owners_count":26082973,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-09T02:00:07.460Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","horned-worm","http","ocaml","webservice"],"created_at":"2024-11-15T15:41:47.475Z","updated_at":"2025-10-09T07:37:12.142Z","avatar_url":"https://github.com/kkazuo.png","language":"OCaml","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Horned_worm\n\nA functional Web app server.\n\nGreatly inspired by [Suave.IO](https://suave.io) and [GIRAFFE](https://github.com/dustinmoris/Giraffe) of F#, this is OCaml implementation.\n\n\n\u003e Sphingids are some of the faster flying insects; some are capable of flying at over 5.3 m/s (12 miles per hour). --- [Wikipedia](https://en.wikipedia.org/wiki/Sphingidae)\n\n\n## Web Parts\n\n### path\n\n```ocaml\nlet app =\n  path \"/hello\"\n  \u003e=\u003e text \"hello, world\"\n```\n\n### path_ci\n\n```ocaml\nlet app =\n  path_ci \"/hello\"\n  \u003e=\u003e text \"hello, world\"\n```\n\n### path_starts\n\n```ocaml\n let app =\n   path_starts \"/hello/\"\n   \u003e=\u003e text \"hello, world\"\n```\n\n### path_starts_ci\n\n```ocaml\nlet app =\n  path_starts_ci \"/hello/\"\n  \u003e=\u003e text \"hello, world\"\n```\n\n### path_regex\n\n```ocaml\nlet app =\n  path_regex \"/hello/wo{3}rld\"\n  \u003e=\u003e text \"hello, world\"\n```\n\n### path_scanf\n\n```ocaml\nlet app =\n  path_scanf \"/%d/%d\" begin fun x y -\u003e\n    text @@ Printf.sprintf \"hello, %d\" (x + y)\n  end\n```\n\n### meth\n\n```ocaml\nlet app =\n  meth `GET\n  \u003e=\u003e text \"hello, world\"\n```\n\n### filter_p\n\n```ocaml\nlet app =\n  filter_p (fun ctx -\u003e true)\n  \u003e=\u003e text \"text.\"\n```\n\n### path_p\n\n```ocaml\nlet app =\n  path_p (fun path -\u003e true)\n  \u003e=\u003e text \"text.\"\n```\n\n### choose\n\n```ocaml\nlet app =\n  choose\n    [ meth `GET\n      \u003e=\u003e choose\n        [ path \"/a\" \u003e=\u003e text \"hello, GET a\"\n        ; path \"/b\" \u003e=\u003e text \"hello, GET b\"\n        ]\n    ; meth `POST \u003e=\u003e text \"hello, POST\"\n    ]\n```\n\n### log\n\n```ocaml\nlet app =\n  log Logs.err (fun ctx m -\u003e m \"error message.\")\n  \u003e=\u003e text \"text.\"\n```\n\n### set_mime_type\n\n```ocaml\nlet app =\n  set_mime_type \"text/plain; charset=utf-8\"\n  \u003e=\u003e text \"text.\"\n```\n\n### set_status\n\n```ocaml\nlet app =\n  set_status `Bad_request\n  \u003e=\u003e text \"text.\"\n```\n\n### set_header\n\n```ocaml\nlet app =\n  set_header \"x-test\" \"my test header\"\n  \u003e=\u003e text \"text.\"\n```\n\n### set_header_unless_exists\n\n```ocaml\nlet app =\n  set_header_unless_exists \"x-test\" \"my test header\"\n  \u003e=\u003e text \"text.\"\n```\n\n### add_header\n\n```ocaml\n let app =\n   add_header \"x-test\" \"my test header\"\n   \u003e=\u003e text \"text.\"\n```\n\n### use_cookie\n\n```ocaml\nlet app =\n  use_cookie\n  \u003e=\u003e text \"text.\"\n```\n\n### set_cookie\n\n```ocaml\nlet app =\n  set_cookie \"key\" \"value\"\n  \u003e=\u003e text \"text.\"\n```\n\n### browse\n\n```ocaml\nlet app =\n  browse \"/etc\"\n```\n\n### browse_file\n\n```ocaml\nlet app =\n  browse_file \"/etc\" \"/hosts\"\n```\n\n### text\n\n```ocaml\nlet app =\n  text \"hello, world.\"\n```\n\n### texts\n\n```ocaml\nlet app =\n  texts [ \"hello\"\n        ; \", world.\"\n        ]\n```\n\n### json\n\n```ocaml\nlet app =\n  json (`Assoc [ \"hello\", `String \"world\"\n               ; \"key\", `Int 1 ])\n```\n\n### respond_string\n\n```ocaml\nlet app =\n  respond_string \"string\"\n```\n\n### respond_strings\n\n```ocaml\nlet app =\n  respond_strings [ \"string\"; \"string\" ]\n```\n\n### respond_file\n\n```ocaml\nlet app =\n  respond_file \"/etc/hosts\"\n```\n\n### simple_cors\n\n```ocaml\nlet app =\n  simple_cors\n  \u003e=\u003e text \"text.\"\n```\n\n### secure_headers\n\n```ocaml\nlet app =\n  secure_headers\n  \u003e=\u003e text \"text.\"\n```\n\n### web_server\n\n- Web_part.t Web app\n- int  Listening port\n\n### run_web_server\n\n- app Web_part.t\n\nSimple Web_part.t runner.\n\n\n## Compose your own parts\n\n```ocaml\nlet yourapp : Web_part.t =\n  fun next ctx -\u003e\n    (* your work here *)\n    if (* should continue *) then\n      let modified_ctx = ctx in\n      begin\n        set_header \"a\" \"b\" \u003e=\u003e\n        set_header \"x\" \"y\"\n      end next modified_ctx\n    else\n      Web_part.fail\n```\n\n\n## How to start\n\nExample:\n\n```ocaml\nopen Core\nopen Async\nopen Horned_worm\n\nlet app =\n  choose\n    [ meth `GET \u003e=\u003e choose\n        [ path \"/\" \u003e=\u003e text \"hello, world\"\n        ; path \"/cookie\" \u003e=\u003e use_cookie \u003e=\u003e begin\n            let key = \"test\" in\n            fun next ctx -\u003e\n              let v = Option.value Http_context.(cookie ctx ~key)\n                  ~default:\"hello cookie\" in\n              begin\n                set_cookie key (v ^ \"!\") \u003e=\u003e\n                text v\n              end next ctx\n          end\n        ; path_scanf \"/%d/%d\" begin fun x y -\u003e\n            text (sprintf \"%d + %d = %d\" x y (x + y))\n          end\n        ; path_scanf \"/json/%s\" begin fun s -\u003e\n            json (`Assoc [\"hello\", `String s])\n          end\n        ]\n    ; meth `POST \u003e=\u003e path \"/\" \u003e=\u003e text \"hello, POST\"\n    ]\n\nlet () =\n  run_web_server app\n```\n\n## Install\n\n    opam install horned_worm\n\n## Status\n\n- alpha\n\nTodo:\n\n- more predefined Web parts.\n- needs query parm receiver.\n- builtin websocket supports.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkkazuo%2Fhorned_worm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkkazuo%2Fhorned_worm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkkazuo%2Fhorned_worm/lists"}