{"id":20037988,"url":"https://github.com/perfectlysoft/perfect-http","last_synced_at":"2025-05-05T06:31:49.867Z","repository":{"id":44901324,"uuid":"62731200","full_name":"PerfectlySoft/Perfect-HTTP","owner":"PerfectlySoft","description":"Base HTTP Support for Perfect.","archived":false,"fork":false,"pushed_at":"2022-01-19T18:00:53.000Z","size":207,"stargazers_count":31,"open_issues_count":4,"forks_count":34,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-04-22T19:19:14.169Z","etag":null,"topics":["httpserver","perfect","server-side-swift","swift"],"latest_commit_sha":null,"homepage":"https://www.perfect.org","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/PerfectlySoft.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-07-06T15:16:42.000Z","updated_at":"2024-01-27T15:01:41.000Z","dependencies_parsed_at":"2022-08-25T14:50:13.552Z","dependency_job_id":null,"html_url":"https://github.com/PerfectlySoft/Perfect-HTTP","commit_stats":null,"previous_names":[],"tags_count":80,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PerfectlySoft%2FPerfect-HTTP","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PerfectlySoft%2FPerfect-HTTP/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PerfectlySoft%2FPerfect-HTTP/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PerfectlySoft%2FPerfect-HTTP/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PerfectlySoft","download_url":"https://codeload.github.com/PerfectlySoft/Perfect-HTTP/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252451585,"owners_count":21749954,"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","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":["httpserver","perfect","server-side-swift","swift"],"created_at":"2024-11-13T10:24:32.418Z","updated_at":"2025-05-05T06:31:49.561Z","avatar_url":"https://github.com/PerfectlySoft.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Perfect-HTTP [简体中文](README.zh_CN.md)\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"http://perfect.org/get-involved.html\" target=\"_blank\"\u003e\n        \u003cimg src=\"http://perfect.org/assets/github/perfect_github_2_0_0.jpg\" alt=\"Get Involed with Perfect!\" width=\"854\" /\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://github.com/PerfectlySoft/Perfect\" target=\"_blank\"\u003e\n        \u003cimg src=\"http://www.perfect.org/github/Perfect_GH_button_1_Star.jpg\" alt=\"Star Perfect On Github\" /\u003e\n    \u003c/a\u003e  \n    \u003ca href=\"http://stackoverflow.com/questions/tagged/perfect\" target=\"_blank\"\u003e\n        \u003cimg src=\"http://www.perfect.org/github/perfect_gh_button_2_SO.jpg\" alt=\"Stack Overflow\" /\u003e\n    \u003c/a\u003e  \n    \u003ca href=\"https://twitter.com/perfectlysoft\" target=\"_blank\"\u003e\n        \u003cimg src=\"http://www.perfect.org/github/Perfect_GH_button_3_twit.jpg\" alt=\"Follow Perfect on Twitter\" /\u003e\n    \u003c/a\u003e  \n    \u003ca href=\"http://perfect.ly\" target=\"_blank\"\u003e\n        \u003cimg src=\"http://www.perfect.org/github/Perfect_GH_button_4_slack.jpg\" alt=\"Join the Perfect Slack\" /\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://developer.apple.com/swift/\" target=\"_blank\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/Swift-4.0-orange.svg?style=flat\" alt=\"Swift 4.0\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://developer.apple.com/swift/\" target=\"_blank\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/Platforms-OS%20X%20%7C%20Linux%20-lightgray.svg?style=flat\" alt=\"Platforms OS X | Linux\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"http://perfect.org/licensing.html\" target=\"_blank\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/License-Apache-lightgrey.svg?style=flat\" alt=\"License Apache\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"http://twitter.com/PerfectlySoft\" target=\"_blank\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/Twitter-@PerfectlySoft-blue.svg?style=flat\" alt=\"PerfectlySoft Twitter\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"http://perfect.ly\" target=\"_blank\"\u003e\n        \u003cimg src=\"http://perfect.ly/badge.svg\" alt=\"Slack Status\"\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n# Base HTTP Support for Perfect\n\nThis repository is a dependency for the Perfect HTTP/1, HTTP/2, and FastCGI servers. Please look at [Perfect HTTPServer](https://github.com/PerfectlySoft/Perfect-HTTPServer) for more details.\n\n## Overview\n\nThe HTTP library provides a set of Enums, Structs, Protocols and functions to handle interactions with HTTP clients. It provides concrete implimentations for the URL routing mechanism. When you are setting up an HTTPServer you will need to import this library to use the Routing functions. Generally:\n\n``` swift\nimport PerfectHTTP\nimport PerfectHTTPServer\n```\n\nAn example of a routing declaration using a closure block as the handler: \n\n``` swift\nvar routes = Routes()\nroutes.add(method: .get, uri: \"/\") {\n\trequest, response in\n\tresponse.appendBody(string: \"\u003chtml\u003e\u003ctitle\u003eHello, world!\u003c/title\u003e\u003cbody\u003eHello, world!\u003c/body\u003e\u003c/html\u003e\")\n\tresponse.completed()\n}\n```\n\nThe handler can be a separate function which takes an HTTPRequest, an HTTPResponse and either completes the response or hands off to a function which does.\n\n``` swift\nfunc helloWorld(request: HTTPRequest, response: HTTPResponse) {\n\tresponse.appendBody(string: \"\u003chtml\u003e\u003ctitle\u003eHello, world!\u003c/title\u003e\u003cbody\u003eHello, world!\u003c/body\u003e\u003c/html\u003e\")\n\t\t.completed()\n}\nroutes.add(method: .get, uri: \"/hello\", handler: helloWorld)\n```\n\nThe routes must be added to the server instance before it is started.\n\n``` swift\ntry HTTPServer.launch(name: \"my.server.ca\", port: port, routes: routes)\n```\n\nRoutes can not be added or modified after a server has started listening for requests.\n\nInside your handler function the HTTPRequest object provides access to all client request information. This includes all  client headers, query parameters, POST body data and other relevant information such as the client IP address and URL variables.\n\nHTTPRequest will handle parsing and decoding all \"application/x-www-form-urlencoded\" as well as \"multipart/form-data\" content type requests. It will make the data for any other content types available in a raw, unparsed form. When handling multipart form data, HTTPRequest will automatically decode the data and create temporary files for any file uploads contained therein. These files will exist until the request ends after which they will be automatically deleted.\n\nAs you build the return values, the HTTPResponse object contains all outgoing response data. This consists of the HTTP status code and message, the HTTP headers, and any response body data. HTTPResponse also contains the ability to stream or push chunks of response data to the client and to complete or terminate the request.\n\n### \u003ca name=\"typed\"\u003e\u003c/a\u003eTyped Routing\n\nIn addition to raw handlers which accept request and response objects, Perfect-HTTP routes also support strongly typed handlers which decode, accept, or return Codable Swift objects.\n\nThe APIs for working with typed routes are very similar to those for working with un-typed routes. The objects for producing typed routes are named `TRoutes` and `TRoute`. The meaning and usage of these objects correspond closely to those of the `Routes` and `Route` objects, respectively. \n\nThe interfaces for these objects are as follows:\n\n```swift\n/// A typed intermediate route handler parameterized on the input and output types.\npublic struct TRoutes\u003cI, O\u003e {\n\t/// Input type alias\n\tpublic typealias InputType = I\n\t/// Output type alias\n\tpublic typealias OutputType = O\n\t/// Init with a base URI and handler.\n\tpublic init(\n\t\tbaseUri u: String,\n\t\thandler t: @escaping (InputType) throws -\u003e OutputType)\n\t/// Add a typed route to this base URI.\n\t@discardableResult\n\tpublic mutating func add\u003cN\u003e(_ route: TRoute\u003cOutputType, N\u003e) -\u003e TRoutes\n\t/// Add other intermediate routes to this base URI.\n\t@discardableResult\n\tpublic mutating func add\u003cN\u003e(_ route: TRoutes\u003cOutputType, N\u003e) -\u003e TRoutes\n\t/// Add a route to this object. The new route will take the output of this route as its input.\n\t@discardableResult\n\tpublic mutating func add\u003cN: Codable\u003e(\n\t\tmethod m: HTTPMethod,\n\t\turi u: String,\n\t\thandler t: @escaping (OutputType) throws -\u003e N) -\u003e TRoutes\n}\n\n/// A typed route handler.\npublic struct TRoute\u003cI, O: Codable\u003e {\n\t/// Input type alias.\n\tpublic typealias InputType = I\n\t/// Output type alias.\n\tpublic typealias OutputType = O\n\t// Init with a method, uri, and handler.\n\tpublic init(\n\t\tmethod m: HTTPMethod,\n\t\turi u: String,\n\t\thandler t: @escaping (InputType) throws -\u003e OutputType)\n\t/// Init with zero or more methods, a uri, and handler.\n\tpublic init(\n\t\tmethods m: [HTTPMethod] = [.get, .post],\n\t\turi u: String,\n\t\thandler t: @escaping (InputType) throws -\u003e OutputType)\n}\n```\n\nJust as with `Routes` and `Route` objects, a `TRoutes` is an intermediate handler and a `TRoute` is a terminal handler.\n\nA `TRoutes` handler can be created accepting either an HTTPRequest object or any other type of object which may be passed down from a previous handler. The first `TRoutes` handler is usually created accepting the HTTPRequest object. This handler in turn processes its input and returns some object which is given to subsequent handlers.\n\nA `TRoute` handler accepts some sort of input type and returns a Codable object. This codable object is serialized to JSON and returned to the client.\n\nThe input to both types of handlers will either be the HTTPRequest, the result of decoding the request body to some Decodable object, or the return value of whatever intermediate handler occurred immediately before. When a handler is defined as receiving a Decodable object, the HTTPRequest body will be automatically decoded into this type. If the body can not be decoded then an Error will be thrown and the error response will be returned to the client. Alternatively, a handler can be defined as accepting the HTTPRequest object but can decode the body itself using the `HTTPRequest.decode` function (described below).\n\n#### Request Body Decode\n\nTwo extensions on the HTTPRequest object aid in decoding the request body. \n\n```swift\n/// Extensions on HTTPRequest which permit the request body to be decoded to a Codable type.\npublic extension HTTPRequest {\n\t/// Decode the request as the desired object.\n\tfunc decode\u003cA: Codable\u003e() throws -\u003e A\n\t/// Identity decode. Used to permit generic code to operate with the HTTPRequest\n\tfunc decode() throws -\u003e Self\n}\n```\n\nThe first function will decode the body into the desired Codable object. If the request's content-type is application/json then the body will be decoded from that JSON. Otherwise, the request's URL encoded GET or POST arguments will be used for the decode. Additionally, any URL variables (described later in this document) will be utilized for the decode. This allows for a mixture of GET/POST arguments and URL variables to be brought together when decoding the object.\n\nNote that when decoding objects from non-JSON request data, nested, non-integral objects are not supported. Objects with array properties are also not supported in this case. \n\n#### Response Error\n\nIf either an intermediate or terminal typed handler experiences an error during processing, they can throw an `HTTPResponseError`. Initializing one of these objects requires both an `HTTPResponseStatus` and a String description of the problem. \n\n```swift\n/// A codable response type indicating an error.\npublic struct HTTPResponseError: Error, Codable, CustomStringConvertible {\n\t/// The HTTP status for the response.\n\tpublic let status: HTTPResponseStatus\n\t/// Textual description of the error.\n\tpublic let description: String\n\t/// Init with status and description.\n\tpublic init(\n\t\tstatus s: HTTPResponseStatus,\n\t\tdescription d: String)\n}\n```\n\n#### Support Extensions\n\nExtensions on `Routes` permits adding `TRoutes` or `TRoute` objects.\n\n```swift\npublic extension Routes {\n\t/// Add routes to this object.\n\tmutating func add\u003cI, O\u003e(_ route: TRoutes\u003cI, O\u003e)\n\t/// Add a route to this object.\n\tmutating func add\u003cI, O\u003e(_ route: TRoute\u003cI, O\u003e)\n}\n```\n\n#### \u003ca name=\"typed_examples\"\u003e\u003c/a\u003eTyped Routing Examples\n\nThe following example shows how Codable objects for a route would be defined and how the typed routes would be added to a `Routes` object.\n\nIn this abbreviated example the intermediate handler for \"/api\" would perform some screening on the request to ensure the client has been authenticated. It would then return to the next handler (which is terminal, in this case) a tuple consisting of the original HTTPRequest object as well as a `SessionInfo` object containing whatever client id had been pulled from the request. The terminal handler \"/api/info/{id}\" would then use this information to complete the request and return the response.\n\n```swift\nstruct SessionInfo: Codable {\n\t//...could be an authentication token, etc.\n\tlet id: String\n}\nstruct RequestResponse: Codable {\n\tstruct Address: Codable {\n\t\tlet street: String\n\t\tlet city: String\n\t\tlet province: String\n\t\tlet country: String\n\t\tlet postalCode: String\n\t}\n\tlet fullName: String\n\tlet address: Address\n}\n// when handlers further down need the request you can pass it along. this is not necessary though\ntypealias RequestSession = (request: HTTPRequest, session: SessionInfo)\n\n// intermediate handler for /api\nfunc checkSession(request: HTTPRequest) throws -\u003e RequestSession {\n\t// one would check the request to make sure it's authorized\n\tlet sessionInfo: SessionInfo = try request.decode() // will throw if request does not include id\n\treturn (request, sessionInfo)\n}\n\n// terminal handler for /api/info/{id}\nfunc userInfo(session: RequestSession) throws -\u003e RequestResponse {\n\t// return the response for this request\n\treturn .init(fullName: \"Justin Trudeau\",\n\t\taddress: .init(\n\t\t\tstreet: \"111 Wellington St\",\n\t\t\tcity: \"Ottawa\",\n\t\t\tprovince: \"Ontario\",\n\t\t\tcountry: \"Canada\",\n\t\t\tpostalCode: \"K1A 0A6\"))\n}\n// root Routes object holding all other routes for this server\nvar routes = Routes()\n// types routes object for the /api URI\nvar apiRoutes = TRoutes(baseUri: \"/api\", handler: checkSession)\n// add terminal handler for the /info/{id} URI suffix\napiRoutes.add(method: .get, uri: \"/info/{id}\", handler: userInfo)\n// add the typed routes to the root\nroutes.add(apiRoutes)\n\n// add routes to server and launch\ntry HTTPServer.launch(name: \"my.server.ca\", port: port, routes: routes)\n```\n\n## More Information\n\nThe following documents contain pertinent information:\n\n[Configuring and Launching HTTPServer](https://www.perfect.org/docs/HTTPServer.html)\n\n[Routing](https://www.perfect.org/docs/routing.html)\n\n[All Perfect Docs](https://www.perfect.org/docs/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fperfectlysoft%2Fperfect-http","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fperfectlysoft%2Fperfect-http","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fperfectlysoft%2Fperfect-http/lists"}