From 5184d6c82760cd597dcf5d30b9622b468f0d9bad Mon Sep 17 00:00:00 2001 From: Steven Klassen Date: Fri, 26 Mar 2021 19:59:48 -0600 Subject: [PATCH] Added configuration for the auto populating headers and auto recognizing the input Closes #21, #22 --- README.md | 74 ++++++++++++++++++++++++++++ Sources/CrestLib/Configuration.swift | 10 +++- Sources/CrestLib/Operation.swift | 21 ++++++-- Sources/crest/main.swift | 9 +++- 4 files changed, 106 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 54f9328..7c3bf00 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,76 @@ # crest Command line REST client + +### Configuration Files + +The operation of this program can be modified by the existance of configuration files. Specifically, +you can place a `.crestconfig.json` file in your home directory, or in the directory from which +you are running `crest`. (Say, for example, the project directory where you are developing your +web service.) In this way you can customize the program operation for the needs of a given +project. + +A configuration file in the local directory will override any settings found in the global file (the one +in your home directory), and many of the settings can also be overridden by command line +options. + +The configuration files are JSON files consisting of a single dictionary where each key is +one of the following, and the contents are as described. + +E.g. + + { + "Private": true, + "URLPrefix": "http://mytestserver.local:8080/api/v2" + } + +#### AutoPopulateRequestHeaders + +By default `crest` adds a number of request headers automatically. They can be overridden +by manually specifying headers of the same name, but you can turn off the auto headers +completely by setting this to `false`. In that case only the headers that are absolutely required +for the HTTP protocol will be added. + +e.g. `"AutoPopulateRequestHeaders": false` (Default is `true`) + +This is also turn off if `--no-auto-headers` is specified on the command line. + +#### AutoRecognizeRequestContent + +By default `crest` will attempt to identify the contents of the standard input and set the +`Content-Type` header appropriately. However, there are the following limitations: + +1. Presently only JSON and XML data are recognized. +2. If the data is too large (by default over 2048 bytes), then it will be sent as chunked data + to avoid keeping the entire contents in memory at once. In this case it will not be + recognized. + + By turning this off no `Content-Type` header will be added automatically, and you will + need to add it manually via the command line options. + + e.g. `"AutoRecognizeRequestContent": false` (Default is `true`) + + This is also turn off if `--no-auto-headers` is specified on the command line. + +#### Private + +By default `crest` adds a `User-Agent` key that includes information about the machine you +are running on. For example: `Crest/1.0.0 (macOS; Version 11.2.3 (Build 20D91); x86_64)`. +If you set `Private` to `true`, then the details will be left out and the `User-Agent` will simply +be reported as `Crest`. (You can override this as well by specifying your own `User_Agent` header +either in the configuration files or via the command line.) + +E.g. `"Private": true` (Default is `false`) + +#### URLPrefix + +This can be used to set a prefix that will automatically be prepended to the URL given on the command +line. This is likely to be most useful in the local `.crestconfig.json` where you can set it to be +the common part of the web service you are testing. This can save you a lot of typing. + +Note that the `URLPrefix` will be ignored if the command line URL begins with `http:` or +`https:`. + +E.g. `"URLPrefix": "http://mytestserver.local:8080/api/v2"` (Default is empty) + +In this example, if you run the command `crest /contract/id871` it will actually perform +a `GET` to the URL `http://mytestserver.local:8080/api/v2/contract/id871`. diff --git a/Sources/CrestLib/Configuration.swift b/Sources/CrestLib/Configuration.swift index c4abd10..1034e06 100644 --- a/Sources/CrestLib/Configuration.swift +++ b/Sources/CrestLib/Configuration.swift @@ -10,7 +10,10 @@ import Foundation public struct Configuration { - var URLPrefix: String? = nil + var autoPopulateRequestHeaders: Bool? = nil + var autoRecognizeRequestContent: Bool? = nil + var isPrivate: Bool? = nil + var urlPrefix: String? = nil static var shared = Configuration() @@ -20,7 +23,10 @@ public struct Configuration { manager.load(file: filename, relativeFrom: .customPath(homeDirectory)) .load(file: filename, relativeFrom: .pwd) .load(overloads) - shared.URLPrefix = manager["URLPrefix"] as? String + shared.autoPopulateRequestHeaders = manager["AutoPopulateRequestHeaders"] as? Bool + shared.autoRecognizeRequestContent = manager["AutoRecognizeRequestContent"] as? Bool + shared.isPrivate = manager["Private"] as? Bool + shared.urlPrefix = manager["URLPrefix"] as? String print("!! config: \(shared)") } diff --git a/Sources/CrestLib/Operation.swift b/Sources/CrestLib/Operation.swift index 6440037..5f781ff 100644 --- a/Sources/CrestLib/Operation.swift +++ b/Sources/CrestLib/Operation.swift @@ -39,7 +39,7 @@ public struct Operation { private func urlAsString() -> String { if url.scheme == nil { - if let prefix = Configuration.shared.URLPrefix { + if let prefix = Configuration.shared.urlPrefix { return prefix + url.absoluteString } } @@ -47,10 +47,19 @@ public struct Operation { } private func addHeadersToRequest(_ request: inout HTTPClient.Request) { - let platform = Platform() request.headers.add(name: "Host", value: "\(request.host):\(request.port)") - request.headers.add(name: "User-Agent", value: "Crest/\(VERSION) (\(platform.operatingSystem); \(platform.operatingSystemVersion); \(platform.hardware))") - request.headers.add(name: "Accept", value: "*/*") + if Configuration.shared.autoPopulateRequestHeaders ?? true { + request.headers.add(name: "User-Agent", value: getUserAgent()) + request.headers.add(name: "Accept", value: "*/*") + } + } + + private func getUserAgent() -> String { + if Configuration.shared.isPrivate ?? false { + return "Crest" + } + let platform = Platform() + return "Crest/\(VERSION) (\(platform.operatingSystem); \(platform.operatingSystemVersion); \(platform.hardware))" } // This "ugliness" is needed for the streaming requests since we need the stream @@ -88,7 +97,9 @@ public struct Operation { } else if (try? XMLDocument(data: data)) != nil { contentType = "application/xml" } - request.headers.add(name: "Content-Type", value: contentType) + if Configuration.shared.autoRecognizeRequestContent ?? true { + request.headers.add(name: "Content-Type", value: contentType) + } request.body = .string(s) return } diff --git a/Sources/crest/main.swift b/Sources/crest/main.swift index 15a997d..0e42bd6 100644 --- a/Sources/crest/main.swift +++ b/Sources/crest/main.swift @@ -11,9 +11,12 @@ struct Crest: ParsableCommand { version: CrestLib.VERSION ) - @Option(name: .shortAndLong, help: "The HTTP method (defaults to GET)") + @Option(name: .shortAndLong, help: "The HTTP method (defaults to GET).") var method: HTTPMethod = .GET + @Flag(help: "Turn off the auto-population of headers.") + var noAutoHeaders = false + @Argument(help: "The URL of the service to contact.") var url: String @@ -37,6 +40,10 @@ struct Crest: ParsableCommand { func commandLineOverrides() -> [String: Any?] { var overrides = [String: Any?]() + if noAutoHeaders { + overrides["AutoPopulateRequestHeaders"] = false + overrides["AutoRecognizeRequestContent"] = false + } return overrides } }