Compare commits

..
Author SHA1 Message Date
Brian Cristante bcb0e62b16 Copy over http-client files 2022-04-28 09:33:03 -04:00
21 changed files with 10969 additions and 837 deletions
+15 -15
View File
@@ -5,8 +5,8 @@ on:
inputs: inputs:
package: package:
required: true required: true
description: 'core, artifact, cache, exec, github, glob, http-client, io, tool-cache' description: 'core, artifact, cache, exec, github, glob, io, tool-cache'
jobs: jobs:
test: test:
runs-on: macos-latest runs-on: macos-latest
@@ -17,40 +17,40 @@ jobs:
- name: verify package exists - name: verify package exists
run: ls packages/${{ github.event.inputs.package }} run: ls packages/${{ github.event.inputs.package }}
- name: Set Node.js 12.x - name: Set Node.js 12.x
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: 12.x node-version: 12.x
- name: npm install - name: npm install
run: npm install run: npm install
- name: bootstrap - name: bootstrap
run: npm run bootstrap run: npm run bootstrap
- name: build - name: build
run: npm run build run: npm run build
- name: test - name: test
run: npm run test run: npm run test
- name: pack - name: pack
run: npm pack run: npm pack
working-directory: packages/${{ github.event.inputs.package }} working-directory: packages/${{ github.event.inputs.package }}
- name: upload artifact - name: upload artifact
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2
with: with:
name: ${{ github.event.inputs.package }} name: ${{ github.event.inputs.package }}
path: packages/${{ github.event.inputs.package }}/*.tgz path: packages/${{ github.event.inputs.package }}/*.tgz
publish: publish:
runs-on: macos-latest runs-on: macos-latest
needs: test needs: test
environment: npm-publish environment: npm-publish
steps: steps:
- name: download artifact - name: download artifact
uses: actions/download-artifact@v2 uses: actions/download-artifact@v2
with: with:
@@ -58,7 +58,7 @@ jobs:
- name: setup authentication - name: setup authentication
run: echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" >> .npmrc run: echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" >> .npmrc
env: env:
NPM_TOKEN: ${{ secrets.TOKEN }} NPM_TOKEN: ${{ secrets.TOKEN }}
- name: publish - name: publish
@@ -68,13 +68,13 @@ jobs:
if: failure() if: failure()
run: | run: |
curl -X POST -H 'Content-type: application/json' --data '{"text":":pb__failed: Failed to publish a new version of ${{ github.event.inputs.package }}"}' $SLACK_WEBHOOK curl -X POST -H 'Content-type: application/json' --data '{"text":":pb__failed: Failed to publish a new version of ${{ github.event.inputs.package }}"}' $SLACK_WEBHOOK
env: env:
SLACK_WEBHOOK: ${{ secrets.SLACK }} SLACK_WEBHOOK: ${{ secrets.SLACK }}
- name: notify slack on success - name: notify slack on success
if: success() if: success()
run: | run: |
curl -X POST -H 'Content-type: application/json' --data '{"text":":dance: Successfully published a new version of ${{ github.event.inputs.package }}"}' $SLACK_WEBHOOK curl -X POST -H 'Content-type: application/json' --data '{"text":":dance: Successfully published a new version of ${{ github.event.inputs.package }}"}' $SLACK_WEBHOOK
env: env:
SLACK_WEBHOOK: ${{ secrets.SLACK }} SLACK_WEBHOOK: ${{ secrets.SLACK }}
-9
View File
@@ -46,15 +46,6 @@ $ npm install @actions/glob
``` ```
<br/> <br/>
:phone: [@actions/http-client](packages/http-client)
A lightweight HTTP client optimized for building actions. Read more [here](packages/http-client)
```bash
$ npm install @actions/http-client
```
<br/>
:pencil2: [@actions/io](packages/io) :pencil2: [@actions/io](packages/io)
Provides disk i/o functions like cp, mv, rmRF, which etc. Read more [here](packages/io) Provides disk i/o functions like cp, mv, rmRF, which etc. Read more [here](packages/io)
@@ -1,10 +1,9 @@
import * as fs from 'fs' import * as fs from 'fs'
import * as os from 'os' import * as os from 'os'
import path from 'path' import path from 'path'
import {summary, SUMMARY_ENV_VAR} from '../src/summary' import {markdownSummary, SUMMARY_ENV_VAR} from '../src/markdown-summary'
const testDirectoryPath = path.join(__dirname, 'test') const testFilePath = path.join(__dirname, 'test', 'test-summary.md')
const testFilePath = path.join(testDirectoryPath, 'test-summary.md')
async function assertSummary(expected: string): Promise<void> { async function assertSummary(expected: string): Promise<void> {
const file = await fs.promises.readFile(testFilePath, {encoding: 'utf8'}) const file = await fs.promises.readFile(testFilePath, {encoding: 'utf8'})
@@ -68,12 +67,11 @@ const fixtures = {
} }
} }
describe('@actions/core/src/summary', () => { describe('@actions/core/src/markdown-summary', () => {
beforeEach(async () => { beforeEach(async () => {
process.env[SUMMARY_ENV_VAR] = testFilePath process.env[SUMMARY_ENV_VAR] = testFilePath
await fs.promises.mkdir(testDirectoryPath, {recursive: true})
await fs.promises.writeFile(testFilePath, '', {encoding: 'utf8'}) await fs.promises.writeFile(testFilePath, '', {encoding: 'utf8'})
summary.emptyBuffer() markdownSummary.emptyBuffer()
}) })
afterAll(async () => { afterAll(async () => {
@@ -82,39 +80,39 @@ describe('@actions/core/src/summary', () => {
it('throws if summary env var is undefined', async () => { it('throws if summary env var is undefined', async () => {
process.env[SUMMARY_ENV_VAR] = undefined process.env[SUMMARY_ENV_VAR] = undefined
const write = summary.addRaw(fixtures.text).write() const write = markdownSummary.addRaw(fixtures.text).write()
await expect(write).rejects.toThrow() await expect(write).rejects.toThrow()
}) })
it('throws if summary file does not exist', async () => { it('throws if summary file does not exist', async () => {
await fs.promises.unlink(testFilePath) await fs.promises.unlink(testFilePath)
const write = summary.addRaw(fixtures.text).write() const write = markdownSummary.addRaw(fixtures.text).write()
await expect(write).rejects.toThrow() await expect(write).rejects.toThrow()
}) })
it('appends text to summary file', async () => { it('appends text to summary file', async () => {
await fs.promises.writeFile(testFilePath, '# ', {encoding: 'utf8'}) await fs.promises.writeFile(testFilePath, '# ', {encoding: 'utf8'})
await summary.addRaw(fixtures.text).write() await markdownSummary.addRaw(fixtures.text).write()
await assertSummary(`# ${fixtures.text}`) await assertSummary(`# ${fixtures.text}`)
}) })
it('overwrites text to summary file', async () => { it('overwrites text to summary file', async () => {
await fs.promises.writeFile(testFilePath, 'overwrite', {encoding: 'utf8'}) await fs.promises.writeFile(testFilePath, 'overwrite', {encoding: 'utf8'})
await summary.addRaw(fixtures.text).write({overwrite: true}) await markdownSummary.addRaw(fixtures.text).write({overwrite: true})
await assertSummary(fixtures.text) await assertSummary(fixtures.text)
}) })
it('appends text with EOL to summary file', async () => { it('appends text with EOL to summary file', async () => {
await fs.promises.writeFile(testFilePath, '# ', {encoding: 'utf8'}) await fs.promises.writeFile(testFilePath, '# ', {encoding: 'utf8'})
await summary.addRaw(fixtures.text, true).write() await markdownSummary.addRaw(fixtures.text, true).write()
await assertSummary(`# ${fixtures.text}${os.EOL}`) await assertSummary(`# ${fixtures.text}${os.EOL}`)
}) })
it('chains appends text to summary file', async () => { it('chains appends text to summary file', async () => {
await fs.promises.writeFile(testFilePath, '', {encoding: 'utf8'}) await fs.promises.writeFile(testFilePath, '', {encoding: 'utf8'})
await summary await markdownSummary
.addRaw(fixtures.text) .addRaw(fixtures.text)
.addRaw(fixtures.text) .addRaw(fixtures.text)
.addRaw(fixtures.text) .addRaw(fixtures.text)
@@ -124,33 +122,33 @@ describe('@actions/core/src/summary', () => {
it('empties buffer after write', async () => { it('empties buffer after write', async () => {
await fs.promises.writeFile(testFilePath, '', {encoding: 'utf8'}) await fs.promises.writeFile(testFilePath, '', {encoding: 'utf8'})
await summary.addRaw(fixtures.text).write() await markdownSummary.addRaw(fixtures.text).write()
await assertSummary(fixtures.text) await assertSummary(fixtures.text)
expect(summary.isEmptyBuffer()).toBe(true) expect(markdownSummary.isEmptyBuffer()).toBe(true)
}) })
it('returns summary buffer as string', () => { it('returns summary buffer as string', () => {
summary.addRaw(fixtures.text) markdownSummary.addRaw(fixtures.text)
expect(summary.stringify()).toEqual(fixtures.text) expect(markdownSummary.stringify()).toEqual(fixtures.text)
}) })
it('return correct values for isEmptyBuffer', () => { it('return correct values for isEmptyBuffer', () => {
summary.addRaw(fixtures.text) markdownSummary.addRaw(fixtures.text)
expect(summary.isEmptyBuffer()).toBe(false) expect(markdownSummary.isEmptyBuffer()).toBe(false)
summary.emptyBuffer() markdownSummary.emptyBuffer()
expect(summary.isEmptyBuffer()).toBe(true) expect(markdownSummary.isEmptyBuffer()).toBe(true)
}) })
it('clears a buffer and summary file', async () => { it('clears a buffer and summary file', async () => {
await fs.promises.writeFile(testFilePath, 'content', {encoding: 'utf8'}) await fs.promises.writeFile(testFilePath, 'content', {encoding: 'utf8'})
await summary.clear() await markdownSummary.clear()
await assertSummary('') await assertSummary('')
expect(summary.isEmptyBuffer()).toBe(true) expect(markdownSummary.isEmptyBuffer()).toBe(true)
}) })
it('adds EOL', async () => { it('adds EOL', async () => {
await summary await markdownSummary
.addRaw(fixtures.text) .addRaw(fixtures.text)
.addEOL() .addEOL()
.write() .write()
@@ -158,37 +156,37 @@ describe('@actions/core/src/summary', () => {
}) })
it('adds a code block without language', async () => { it('adds a code block without language', async () => {
await summary.addCodeBlock(fixtures.code).write() await markdownSummary.addCodeBlock(fixtures.code).write()
const expected = `<pre><code>func fork() {\n for {\n go fork()\n }\n}</code></pre>${os.EOL}` const expected = `<pre><code>func fork() {\n for {\n go fork()\n }\n}</code></pre>${os.EOL}`
await assertSummary(expected) await assertSummary(expected)
}) })
it('adds a code block with a language', async () => { it('adds a code block with a language', async () => {
await summary.addCodeBlock(fixtures.code, 'go').write() await markdownSummary.addCodeBlock(fixtures.code, 'go').write()
const expected = `<pre lang="go"><code>func fork() {\n for {\n go fork()\n }\n}</code></pre>${os.EOL}` const expected = `<pre lang="go"><code>func fork() {\n for {\n go fork()\n }\n}</code></pre>${os.EOL}`
await assertSummary(expected) await assertSummary(expected)
}) })
it('adds an unordered list', async () => { it('adds an unordered list', async () => {
await summary.addList(fixtures.list).write() await markdownSummary.addList(fixtures.list).write()
const expected = `<ul><li>foo</li><li>bar</li><li>baz</li><li>💣</li></ul>${os.EOL}` const expected = `<ul><li>foo</li><li>bar</li><li>baz</li><li>💣</li></ul>${os.EOL}`
await assertSummary(expected) await assertSummary(expected)
}) })
it('adds an ordered list', async () => { it('adds an ordered list', async () => {
await summary.addList(fixtures.list, true).write() await markdownSummary.addList(fixtures.list, true).write()
const expected = `<ol><li>foo</li><li>bar</li><li>baz</li><li>💣</li></ol>${os.EOL}` const expected = `<ol><li>foo</li><li>bar</li><li>baz</li><li>💣</li></ol>${os.EOL}`
await assertSummary(expected) await assertSummary(expected)
}) })
it('adds a table', async () => { it('adds a table', async () => {
await summary.addTable(fixtures.table).write() await markdownSummary.addTable(fixtures.table).write()
const expected = `<table><tr><th>foo</th><th>bar</th><th>baz</th><td rowspan="3">tall</td></tr><tr><td>one</td><td>two</td><td>three</td></tr><tr><td colspan="3">wide</td></tr></table>${os.EOL}` const expected = `<table><tr><th>foo</th><th>bar</th><th>baz</th><td rowspan="3">tall</td></tr><tr><td>one</td><td>two</td><td>three</td></tr><tr><td colspan="3">wide</td></tr></table>${os.EOL}`
await assertSummary(expected) await assertSummary(expected)
}) })
it('adds a details element', async () => { it('adds a details element', async () => {
await summary await markdownSummary
.addDetails(fixtures.details.label, fixtures.details.content) .addDetails(fixtures.details.label, fixtures.details.content)
.write() .write()
const expected = `<details><summary>open me</summary>🎉 surprise</details>${os.EOL}` const expected = `<details><summary>open me</summary>🎉 surprise</details>${os.EOL}`
@@ -196,13 +194,13 @@ describe('@actions/core/src/summary', () => {
}) })
it('adds an image with alt text', async () => { it('adds an image with alt text', async () => {
await summary.addImage(fixtures.img.src, fixtures.img.alt).write() await markdownSummary.addImage(fixtures.img.src, fixtures.img.alt).write()
const expected = `<img src="https://github.com/actions.png" alt="actions logo">${os.EOL}` const expected = `<img src="https://github.com/actions.png" alt="actions logo">${os.EOL}`
await assertSummary(expected) await assertSummary(expected)
}) })
it('adds an image with custom dimensions', async () => { it('adds an image with custom dimensions', async () => {
await summary await markdownSummary
.addImage(fixtures.img.src, fixtures.img.alt, fixtures.img.options) .addImage(fixtures.img.src, fixtures.img.alt, fixtures.img.options)
.write() .write()
const expected = `<img src="https://github.com/actions.png" alt="actions logo" width="32" height="32">${os.EOL}` const expected = `<img src="https://github.com/actions.png" alt="actions logo" width="32" height="32">${os.EOL}`
@@ -210,7 +208,7 @@ describe('@actions/core/src/summary', () => {
}) })
it('adds an image with custom dimensions', async () => { it('adds an image with custom dimensions', async () => {
await summary await markdownSummary
.addImage(fixtures.img.src, fixtures.img.alt, fixtures.img.options) .addImage(fixtures.img.src, fixtures.img.alt, fixtures.img.options)
.write() .write()
const expected = `<img src="https://github.com/actions.png" alt="actions logo" width="32" height="32">${os.EOL}` const expected = `<img src="https://github.com/actions.png" alt="actions logo" width="32" height="32">${os.EOL}`
@@ -219,21 +217,21 @@ describe('@actions/core/src/summary', () => {
it('adds headings h1...h6', async () => { it('adds headings h1...h6', async () => {
for (const i of [1, 2, 3, 4, 5, 6]) { for (const i of [1, 2, 3, 4, 5, 6]) {
summary.addHeading('heading', i) markdownSummary.addHeading('heading', i)
} }
await summary.write() await markdownSummary.write()
const expected = `<h1>heading</h1>${os.EOL}<h2>heading</h2>${os.EOL}<h3>heading</h3>${os.EOL}<h4>heading</h4>${os.EOL}<h5>heading</h5>${os.EOL}<h6>heading</h6>${os.EOL}` const expected = `<h1>heading</h1>${os.EOL}<h2>heading</h2>${os.EOL}<h3>heading</h3>${os.EOL}<h4>heading</h4>${os.EOL}<h5>heading</h5>${os.EOL}<h6>heading</h6>${os.EOL}`
await assertSummary(expected) await assertSummary(expected)
}) })
it('adds h1 if heading level not specified', async () => { it('adds h1 if heading level not specified', async () => {
await summary.addHeading('heading').write() await markdownSummary.addHeading('heading').write()
const expected = `<h1>heading</h1>${os.EOL}` const expected = `<h1>heading</h1>${os.EOL}`
await assertSummary(expected) await assertSummary(expected)
}) })
it('uses h1 if heading level is garbage or out of range', async () => { it('uses h1 if heading level is garbage or out of range', async () => {
await summary await markdownSummary
.addHeading('heading', 'foobar') .addHeading('heading', 'foobar')
.addHeading('heading', 1337) .addHeading('heading', 1337)
.addHeading('heading', -1) .addHeading('heading', -1)
@@ -244,31 +242,35 @@ describe('@actions/core/src/summary', () => {
}) })
it('adds a separator', async () => { it('adds a separator', async () => {
await summary.addSeparator().write() await markdownSummary.addSeparator().write()
const expected = `<hr>${os.EOL}` const expected = `<hr>${os.EOL}`
await assertSummary(expected) await assertSummary(expected)
}) })
it('adds a break', async () => { it('adds a break', async () => {
await summary.addBreak().write() await markdownSummary.addBreak().write()
const expected = `<br>${os.EOL}` const expected = `<br>${os.EOL}`
await assertSummary(expected) await assertSummary(expected)
}) })
it('adds a quote', async () => { it('adds a quote', async () => {
await summary.addQuote(fixtures.quote.text).write() await markdownSummary.addQuote(fixtures.quote.text).write()
const expected = `<blockquote>Where the world builds software</blockquote>${os.EOL}` const expected = `<blockquote>Where the world builds software</blockquote>${os.EOL}`
await assertSummary(expected) await assertSummary(expected)
}) })
it('adds a quote with citation', async () => { it('adds a quote with citation', async () => {
await summary.addQuote(fixtures.quote.text, fixtures.quote.cite).write() await markdownSummary
.addQuote(fixtures.quote.text, fixtures.quote.cite)
.write()
const expected = `<blockquote cite="https://github.com/about">Where the world builds software</blockquote>${os.EOL}` const expected = `<blockquote cite="https://github.com/about">Where the world builds software</blockquote>${os.EOL}`
await assertSummary(expected) await assertSummary(expected)
}) })
it('adds a link with href', async () => { it('adds a link with href', async () => {
await summary.addLink(fixtures.link.text, fixtures.link.href).write() await markdownSummary
.addLink(fixtures.link.text, fixtures.link.href)
.write()
const expected = `<a href="https://github.com/">GitHub</a>${os.EOL}` const expected = `<a href="https://github.com/">GitHub</a>${os.EOL}`
await assertSummary(expected) await assertSummary(expected)
}) })
+2 -2
View File
@@ -361,6 +361,6 @@ export async function getIDToken(aud?: string): Promise<string> {
} }
/** /**
* Summary exports * Markdown summary exports
*/ */
export {summary} from './summary' export {markdownSummary} from './markdown-summary'
@@ -4,7 +4,7 @@ const {access, appendFile, writeFile} = promises
export const SUMMARY_ENV_VAR = 'GITHUB_STEP_SUMMARY' export const SUMMARY_ENV_VAR = 'GITHUB_STEP_SUMMARY'
export const SUMMARY_DOCS_URL = export const SUMMARY_DOCS_URL =
'https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-job-summary' 'https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-markdown-summary'
export type SummaryTableRow = (SummaryTableCell | string)[] export type SummaryTableRow = (SummaryTableCell | string)[]
@@ -51,7 +51,7 @@ export interface SummaryWriteOptions {
overwrite?: boolean overwrite?: boolean
} }
class Summary { class MarkdownSummary {
private _buffer: string private _buffer: string
private _filePath?: string private _filePath?: string
@@ -73,7 +73,7 @@ class Summary {
const pathFromEnv = process.env[SUMMARY_ENV_VAR] const pathFromEnv = process.env[SUMMARY_ENV_VAR]
if (!pathFromEnv) { if (!pathFromEnv) {
throw new Error( throw new Error(
`Unable to find environment variable for $${SUMMARY_ENV_VAR}. Check if your runtime environment supports job summaries.` `Unable to find environment variable for $${SUMMARY_ENV_VAR}. Check if your runtime environment supports markdown summaries.`
) )
} }
@@ -119,9 +119,9 @@ class Summary {
* *
* @param {SummaryWriteOptions} [options] (optional) options for write operation * @param {SummaryWriteOptions} [options] (optional) options for write operation
* *
* @returns {Promise<Summary>} summary instance * @returns {Promise<MarkdownSummary>} markdown summary instance
*/ */
async write(options?: SummaryWriteOptions): Promise<Summary> { async write(options?: SummaryWriteOptions): Promise<MarkdownSummary> {
const overwrite = !!options?.overwrite const overwrite = !!options?.overwrite
const filePath = await this.filePath() const filePath = await this.filePath()
const writeFunc = overwrite ? writeFile : appendFile const writeFunc = overwrite ? writeFile : appendFile
@@ -132,9 +132,9 @@ class Summary {
/** /**
* Clears the summary buffer and wipes the summary file * Clears the summary buffer and wipes the summary file
* *
* @returns {Summary} summary instance * @returns {MarkdownSummary} markdown summary instance
*/ */
async clear(): Promise<Summary> { async clear(): Promise<MarkdownSummary> {
return this.emptyBuffer().write({overwrite: true}) return this.emptyBuffer().write({overwrite: true})
} }
@@ -159,9 +159,9 @@ class Summary {
/** /**
* Resets the summary buffer without writing to summary file * Resets the summary buffer without writing to summary file
* *
* @returns {Summary} summary instance * @returns {MarkdownSummary} markdown summary instance
*/ */
emptyBuffer(): Summary { emptyBuffer(): MarkdownSummary {
this._buffer = '' this._buffer = ''
return this return this
} }
@@ -172,9 +172,9 @@ class Summary {
* @param {string} text content to add * @param {string} text content to add
* @param {boolean} [addEOL=false] (optional) append an EOL to the raw text (default: false) * @param {boolean} [addEOL=false] (optional) append an EOL to the raw text (default: false)
* *
* @returns {Summary} summary instance * @returns {MarkdownSummary} markdown summary instance
*/ */
addRaw(text: string, addEOL = false): Summary { addRaw(text: string, addEOL = false): MarkdownSummary {
this._buffer += text this._buffer += text
return addEOL ? this.addEOL() : this return addEOL ? this.addEOL() : this
} }
@@ -182,9 +182,9 @@ class Summary {
/** /**
* Adds the operating system-specific end-of-line marker to the buffer * Adds the operating system-specific end-of-line marker to the buffer
* *
* @returns {Summary} summary instance * @returns {MarkdownSummary} markdown summary instance
*/ */
addEOL(): Summary { addEOL(): MarkdownSummary {
return this.addRaw(EOL) return this.addRaw(EOL)
} }
@@ -194,9 +194,9 @@ class Summary {
* @param {string} code content to render within fenced code block * @param {string} code content to render within fenced code block
* @param {string} lang (optional) language to syntax highlight code * @param {string} lang (optional) language to syntax highlight code
* *
* @returns {Summary} summary instance * @returns {MarkdownSummary} markdown summary instance
*/ */
addCodeBlock(code: string, lang?: string): Summary { addCodeBlock(code: string, lang?: string): MarkdownSummary {
const attrs = { const attrs = {
...(lang && {lang}) ...(lang && {lang})
} }
@@ -210,9 +210,9 @@ class Summary {
* @param {string[]} items list of items to render * @param {string[]} items list of items to render
* @param {boolean} [ordered=false] (optional) if the rendered list should be ordered or not (default: false) * @param {boolean} [ordered=false] (optional) if the rendered list should be ordered or not (default: false)
* *
* @returns {Summary} summary instance * @returns {MarkdownSummary} markdown summary instance
*/ */
addList(items: string[], ordered = false): Summary { addList(items: string[], ordered = false): MarkdownSummary {
const tag = ordered ? 'ol' : 'ul' const tag = ordered ? 'ol' : 'ul'
const listItems = items.map(item => this.wrap('li', item)).join('') const listItems = items.map(item => this.wrap('li', item)).join('')
const element = this.wrap(tag, listItems) const element = this.wrap(tag, listItems)
@@ -224,9 +224,9 @@ class Summary {
* *
* @param {SummaryTableCell[]} rows table rows * @param {SummaryTableCell[]} rows table rows
* *
* @returns {Summary} summary instance * @returns {MarkdownSummary} markdown summary instance
*/ */
addTable(rows: SummaryTableRow[]): Summary { addTable(rows: SummaryTableRow[]): MarkdownSummary {
const tableBody = rows const tableBody = rows
.map(row => { .map(row => {
const cells = row const cells = row
@@ -260,9 +260,9 @@ class Summary {
* @param {string} label text for the closed state * @param {string} label text for the closed state
* @param {string} content collapsable content * @param {string} content collapsable content
* *
* @returns {Summary} summary instance * @returns {MarkdownSummary} markdown summary instance
*/ */
addDetails(label: string, content: string): Summary { addDetails(label: string, content: string): MarkdownSummary {
const element = this.wrap('details', this.wrap('summary', label) + content) const element = this.wrap('details', this.wrap('summary', label) + content)
return this.addRaw(element).addEOL() return this.addRaw(element).addEOL()
} }
@@ -274,9 +274,13 @@ class Summary {
* @param {string} alt text description of the image * @param {string} alt text description of the image
* @param {SummaryImageOptions} options (optional) addition image attributes * @param {SummaryImageOptions} options (optional) addition image attributes
* *
* @returns {Summary} summary instance * @returns {MarkdownSummary} markdown summary instance
*/ */
addImage(src: string, alt: string, options?: SummaryImageOptions): Summary { addImage(
src: string,
alt: string,
options?: SummaryImageOptions
): MarkdownSummary {
const {width, height} = options || {} const {width, height} = options || {}
const attrs = { const attrs = {
...(width && {width}), ...(width && {width}),
@@ -293,9 +297,9 @@ class Summary {
* @param {string} text heading text * @param {string} text heading text
* @param {number | string} [level=1] (optional) the heading level, default: 1 * @param {number | string} [level=1] (optional) the heading level, default: 1
* *
* @returns {Summary} summary instance * @returns {MarkdownSummary} markdown summary instance
*/ */
addHeading(text: string, level?: number | string): Summary { addHeading(text: string, level?: number | string): MarkdownSummary {
const tag = `h${level}` const tag = `h${level}`
const allowedTag = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(tag) const allowedTag = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(tag)
? tag ? tag
@@ -307,9 +311,9 @@ class Summary {
/** /**
* Adds an HTML thematic break (<hr>) to the summary buffer * Adds an HTML thematic break (<hr>) to the summary buffer
* *
* @returns {Summary} summary instance * @returns {MarkdownSummary} markdown summary instance
*/ */
addSeparator(): Summary { addSeparator(): MarkdownSummary {
const element = this.wrap('hr', null) const element = this.wrap('hr', null)
return this.addRaw(element).addEOL() return this.addRaw(element).addEOL()
} }
@@ -317,9 +321,9 @@ class Summary {
/** /**
* Adds an HTML line break (<br>) to the summary buffer * Adds an HTML line break (<br>) to the summary buffer
* *
* @returns {Summary} summary instance * @returns {MarkdownSummary} markdown summary instance
*/ */
addBreak(): Summary { addBreak(): MarkdownSummary {
const element = this.wrap('br', null) const element = this.wrap('br', null)
return this.addRaw(element).addEOL() return this.addRaw(element).addEOL()
} }
@@ -330,9 +334,9 @@ class Summary {
* @param {string} text quote text * @param {string} text quote text
* @param {string} cite (optional) citation url * @param {string} cite (optional) citation url
* *
* @returns {Summary} summary instance * @returns {MarkdownSummary} markdown summary instance
*/ */
addQuote(text: string, cite?: string): Summary { addQuote(text: string, cite?: string): MarkdownSummary {
const attrs = { const attrs = {
...(cite && {cite}) ...(cite && {cite})
} }
@@ -346,13 +350,13 @@ class Summary {
* @param {string} text link text/content * @param {string} text link text/content
* @param {string} href hyperlink * @param {string} href hyperlink
* *
* @returns {Summary} summary instance * @returns {MarkdownSummary} markdown summary instance
*/ */
addLink(text: string, href: string): Summary { addLink(text: string, href: string): MarkdownSummary {
const element = this.wrap('a', text, {href}) const element = this.wrap('a', text, {href})
return this.addRaw(element).addEOL() return this.addRaw(element).addEOL()
} }
} }
// singleton export // singleton export
export const summary = new Summary() export const markdownSummary = new MarkdownSummary()
+3
View File
@@ -1,2 +1,5 @@
_out
node_modules
.DS_Store
testoutput.txt testoutput.txt
npm-debug.log npm-debug.log
+19 -13
View File
@@ -1,11 +1,18 @@
# `@actions/http-client`
A lightweight HTTP client optimized for building actions. <p align="center">
<img src="actions.png">
</p>
# Actions Http-Client
[![Http Status](https://github.com/actions/http-client/workflows/http-tests/badge.svg)](https://github.com/actions/http-client/actions)
A lightweight HTTP client optimized for use with actions, TypeScript with generics and async await.
## Features ## Features
- HTTP client with TypeScript generics and async/await/Promises - HTTP client with TypeScript generics and async/await/Promises
- Typings included! - Typings included so no need to acquire separately (great for intellisense and no versioning drift)
- [Proxy support](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/about-self-hosted-runners#using-a-proxy-server-with-self-hosted-runners) just works with actions and the runner - [Proxy support](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/about-self-hosted-runners#using-a-proxy-server-with-self-hosted-runners) just works with actions and the runner
- Targets ES2019 (runner runs actions with node 12+). Only supported on node 12+. - Targets ES2019 (runner runs actions with node 12+). Only supported on node 12+.
- Basic, Bearer and PAT Support out of the box. Extensible handlers for others. - Basic, Bearer and PAT Support out of the box. Extensible handlers for others.
@@ -21,7 +28,7 @@ npm install @actions/http-client --save
## Samples ## Samples
See the [tests](./__tests__) for detailed examples. See the [HTTP](./__tests__) tests for detailed examples.
## Errors ## Errors
@@ -32,13 +39,13 @@ The HTTP client does not throw unless truly exceptional.
* A request that successfully executes resulting in a 404, 500 etc... will return a response object with a status code and a body. * A request that successfully executes resulting in a 404, 500 etc... will return a response object with a status code and a body.
* Redirects (3xx) will be followed by default. * Redirects (3xx) will be followed by default.
See the [tests](./__tests__) for detailed examples. See [HTTP tests](./__tests__) for detailed examples.
## Debugging ## Debugging
To enable detailed console logging of all HTTP requests and responses, set the NODE_DEBUG environment varible: To enable detailed console logging of all HTTP requests and responses, set the NODE_DEBUG environment varible:
```shell ```
export NODE_DEBUG=http export NODE_DEBUG=http
``` ```
@@ -56,18 +63,17 @@ We welcome PRs. Please create an issue and if applicable, a design before proce
once: once:
``` ```bash
npm install $ npm install
``` ```
To build: To build:
``` ```bash
npm run build $ npm run build
``` ```
To run all tests: To run all tests:
```bash
``` $ npm test
npm test
``` ```
-10
View File
@@ -1,15 +1,5 @@
## Releases ## Releases
## 2.0.0
- The package is now compiled with TypeScript's [`strict` compiler setting](https://www.typescriptlang.org/tsconfig#strict). To comply with stricter rules:
- Some exported types now include `| null` or `| undefined`, matching their actual behavior.
- Types implementing the method `RequestHandler.handleAuthentication()` now throw an `Error` rather than returning `null` if they do not support handling an HTTP 401 response. Callers can still use `canHandleAuthentication()` to determine if this handling is supported or not.
- Types using `any` have been scoped to more specific types.
- Following TypeScript's naming conventions, exported interfaces no longer begin with the prefix `I-`.
- Delete the `IHttpClientResponse` interface in favor of the `HttpClientResponse` class.
- Delete the `IHeaders` interface in favor of `http.OutgoingHttpHeaders`.
- The source code of the package was moved to build with [actions/toolkit](https://github.com/actions/toolkit).
## 1.0.11 ## 1.0.11
Contains a bug fix where proxy is defined without a user and password. see [PR here](https://github.com/actions/http-client/pull/42) Contains a bug fix where proxy is defined without a user and password. see [PR here](https://github.com/actions/http-client/pull/42)
+26 -38
View File
@@ -1,5 +1,5 @@
import * as httpm from '../lib' import * as httpm from '../_out'
import * as am from '../lib/auth' import * as am from '../_out/auth'
describe('auth', () => { describe('auth', () => {
beforeEach(() => {}) beforeEach(() => {})
@@ -7,21 +7,17 @@ describe('auth', () => {
afterEach(() => {}) afterEach(() => {})
it('does basic http get request with basic auth', async () => { it('does basic http get request with basic auth', async () => {
const bh: am.BasicCredentialHandler = new am.BasicCredentialHandler( let bh: am.BasicCredentialHandler = new am.BasicCredentialHandler(
'johndoe', 'johndoe',
'password' 'password'
) )
const http: httpm.HttpClient = new httpm.HttpClient('http-client-tests', [ let http: httpm.HttpClient = new httpm.HttpClient('http-client-tests', [bh])
bh let res: httpm.HttpClientResponse = await http.get('http://httpbin.org/get')
])
const res: httpm.HttpClientResponse = await http.get(
'http://httpbin.org/get'
)
expect(res.message.statusCode).toBe(200) expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody() let body: string = await res.readBody()
const obj = JSON.parse(body) let obj: any = JSON.parse(body)
const auth: string = obj.headers.Authorization let auth: string = obj.headers.Authorization
const creds: string = Buffer.from( let creds: string = Buffer.from(
auth.substring('Basic '.length), auth.substring('Basic '.length),
'base64' 'base64'
).toString() ).toString()
@@ -30,44 +26,36 @@ describe('auth', () => {
}) })
it('does basic http get request with pat token auth', async () => { it('does basic http get request with pat token auth', async () => {
const token = 'scbfb44vxzku5l4xgc3qfazn3lpk4awflfryc76esaiq7aypcbhs' let token: string = 'scbfb44vxzku5l4xgc3qfazn3lpk4awflfryc76esaiq7aypcbhs'
const ph: am.PersonalAccessTokenCredentialHandler = new am.PersonalAccessTokenCredentialHandler( let ph: am.PersonalAccessTokenCredentialHandler = new am.PersonalAccessTokenCredentialHandler(
token token
) )
const http: httpm.HttpClient = new httpm.HttpClient('http-client-tests', [ let http: httpm.HttpClient = new httpm.HttpClient('http-client-tests', [ph])
ph let res: httpm.HttpClientResponse = await http.get('http://httpbin.org/get')
])
const res: httpm.HttpClientResponse = await http.get(
'http://httpbin.org/get'
)
expect(res.message.statusCode).toBe(200) expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody() let body: string = await res.readBody()
const obj = JSON.parse(body) let obj: any = JSON.parse(body)
const auth: string = obj.headers.Authorization let auth: string = obj.headers.Authorization
const creds: string = Buffer.from( let creds: string = Buffer.from(
auth.substring('Basic '.length), auth.substring('Basic '.length),
'base64' 'base64'
).toString() ).toString()
expect(creds).toBe(`PAT:${token}`) expect(creds).toBe('PAT:' + token)
expect(obj.url).toBe('http://httpbin.org/get') expect(obj.url).toBe('http://httpbin.org/get')
}) })
it('does basic http get request with pat token auth', async () => { it('does basic http get request with pat token auth', async () => {
const token = 'scbfb44vxzku5l4xgc3qfazn3lpk4awflfryc76esaiq7aypcbhs' let token: string = 'scbfb44vxzku5l4xgc3qfazn3lpk4awflfryc76esaiq7aypcbhs'
const ph: am.BearerCredentialHandler = new am.BearerCredentialHandler(token) let ph: am.BearerCredentialHandler = new am.BearerCredentialHandler(token)
const http: httpm.HttpClient = new httpm.HttpClient('http-client-tests', [ let http: httpm.HttpClient = new httpm.HttpClient('http-client-tests', [ph])
ph let res: httpm.HttpClientResponse = await http.get('http://httpbin.org/get')
])
const res: httpm.HttpClientResponse = await http.get(
'http://httpbin.org/get'
)
expect(res.message.statusCode).toBe(200) expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody() let body: string = await res.readBody()
const obj = JSON.parse(body) let obj: any = JSON.parse(body)
const auth: string = obj.headers.Authorization let auth: string = obj.headers.Authorization
expect(auth).toBe(`Bearer ${token}`) expect(auth).toBe('Bearer ' + token)
expect(obj.url).toBe('http://httpbin.org/get') expect(obj.url).toBe('http://httpbin.org/get')
}) })
}) })
+153 -152
View File
@@ -1,10 +1,9 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ import * as httpm from '../_out'
import * as ifm from '../_out/interfaces'
import * as httpm from '..'
import * as path from 'path' import * as path from 'path'
import * as fs from 'fs' import * as fs from 'fs'
const sampleFilePath: string = path.join(__dirname, 'testoutput.txt') let sampleFilePath: string = path.join(__dirname, 'testoutput.txt')
interface HttpBinData { interface HttpBinData {
url: string url: string
@@ -24,7 +23,7 @@ describe('basics', () => {
afterEach(() => {}) afterEach(() => {})
it('constructs', () => { it('constructs', () => {
const http: httpm.HttpClient = new httpm.HttpClient('thttp-client-tests') let http: httpm.HttpClient = new httpm.HttpClient('thttp-client-tests')
expect(http).toBeDefined() expect(http).toBeDefined()
}) })
@@ -40,259 +39,264 @@ describe('basics', () => {
// "url": "https://httpbin.org/get" // "url": "https://httpbin.org/get"
// } // }
it('does basic http get request', async () => { it('does basic http get request', async done => {
const res: httpm.HttpClientResponse = await _http.get( let res: httpm.HttpClientResponse = await _http.get(
'http://httpbin.org/get' 'http://httpbin.org/get'
) )
expect(res.message.statusCode).toBe(200) expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody() let body: string = await res.readBody()
const obj = JSON.parse(body) let obj: any = JSON.parse(body)
expect(obj.url).toBe('http://httpbin.org/get') expect(obj.url).toBe('http://httpbin.org/get')
expect(obj.headers['User-Agent']).toBeTruthy() expect(obj.headers['User-Agent']).toBeTruthy()
done()
}) })
it('does basic http get request with no user agent', async () => { it('does basic http get request with no user agent', async done => {
const http: httpm.HttpClient = new httpm.HttpClient() let http: httpm.HttpClient = new httpm.HttpClient()
const res: httpm.HttpClientResponse = await http.get( let res: httpm.HttpClientResponse = await http.get('http://httpbin.org/get')
'http://httpbin.org/get'
)
expect(res.message.statusCode).toBe(200) expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody() let body: string = await res.readBody()
const obj = JSON.parse(body) let obj: any = JSON.parse(body)
expect(obj.url).toBe('http://httpbin.org/get') expect(obj.url).toBe('http://httpbin.org/get')
expect(obj.headers['User-Agent']).toBeFalsy() expect(obj.headers['User-Agent']).toBeFalsy()
done()
}) })
it('does basic https get request', async () => { it('does basic https get request', async done => {
const res: httpm.HttpClientResponse = await _http.get( let res: httpm.HttpClientResponse = await _http.get(
'https://httpbin.org/get' 'https://httpbin.org/get'
) )
expect(res.message.statusCode).toBe(200) expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody() let body: string = await res.readBody()
const obj = JSON.parse(body) let obj: any = JSON.parse(body)
expect(obj.url).toBe('https://httpbin.org/get') expect(obj.url).toBe('https://httpbin.org/get')
done()
}) })
it('does basic http get request with default headers', async () => { it('does basic http get request with default headers', async done => {
const http: httpm.HttpClient = new httpm.HttpClient( let http: httpm.HttpClient = new httpm.HttpClient('http-client-tests', [], {
'http-client-tests', headers: {
[], Accept: 'application/json',
{ 'Content-Type': 'application/json'
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
}
} }
) })
const res: httpm.HttpClientResponse = await http.get( let res: httpm.HttpClientResponse = await http.get('http://httpbin.org/get')
'http://httpbin.org/get'
)
expect(res.message.statusCode).toBe(200) expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody() let body: string = await res.readBody()
const obj = JSON.parse(body) let obj: any = JSON.parse(body)
expect(obj.headers.Accept).toBe('application/json') expect(obj.headers.Accept).toBe('application/json')
expect(obj.headers['Content-Type']).toBe('application/json') expect(obj.headers['Content-Type']).toBe('application/json')
expect(obj.url).toBe('http://httpbin.org/get') expect(obj.url).toBe('http://httpbin.org/get')
done()
}) })
it('does basic http get request with merged headers', async () => { it('does basic http get request with merged headers', async done => {
const http: httpm.HttpClient = new httpm.HttpClient( let http: httpm.HttpClient = new httpm.HttpClient('http-client-tests', [], {
'http-client-tests', headers: {
[], Accept: 'application/json',
{ 'Content-Type': 'application/json'
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
}
} }
) })
const res: httpm.HttpClientResponse = await http.get( let res: httpm.HttpClientResponse = await http.get(
'http://httpbin.org/get', 'http://httpbin.org/get',
{ {
'content-type': 'application/x-www-form-urlencoded' 'content-type': 'application/x-www-form-urlencoded'
} }
) )
expect(res.message.statusCode).toBe(200) expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody() let body: string = await res.readBody()
const obj = JSON.parse(body) let obj: any = JSON.parse(body)
expect(obj.headers.Accept).toBe('application/json') expect(obj.headers.Accept).toBe('application/json')
expect(obj.headers['Content-Type']).toBe( expect(obj.headers['Content-Type']).toBe(
'application/x-www-form-urlencoded' 'application/x-www-form-urlencoded'
) )
expect(obj.url).toBe('http://httpbin.org/get') expect(obj.url).toBe('http://httpbin.org/get')
done()
}) })
it('pipes a get request', async () => { it('pipes a get request', () => {
return new Promise<void>(async resolve => { return new Promise<string>(async (resolve, reject) => {
const file = fs.createWriteStream(sampleFilePath) let file: NodeJS.WritableStream = fs.createWriteStream(sampleFilePath)
;(await _http.get('https://httpbin.org/get')).message ;(await _http.get('https://httpbin.org/get')).message
.pipe(file) .pipe(file)
.on('close', () => { .on('close', () => {
const body: string = fs.readFileSync(sampleFilePath).toString() let body: string = fs.readFileSync(sampleFilePath).toString()
const obj = JSON.parse(body) let obj: any = JSON.parse(body)
expect(obj.url).toBe('https://httpbin.org/get') expect(obj.url).toBe('https://httpbin.org/get')
resolve() resolve()
}) })
}) })
}) })
it('does basic get request with redirects', async () => { it('does basic get request with redirects', async done => {
const res: httpm.HttpClientResponse = await _http.get( let res: httpm.HttpClientResponse = await _http.get(
`https://httpbin.org/redirect-to?url=${encodeURIComponent( 'https://httpbin.org/redirect-to?url=' +
'https://httpbin.org/get' encodeURIComponent('https://httpbin.org/get')
)}`
) )
expect(res.message.statusCode).toBe(200) expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody() let body: string = await res.readBody()
const obj = JSON.parse(body) let obj: any = JSON.parse(body)
expect(obj.url).toBe('https://httpbin.org/get') expect(obj.url).toBe('https://httpbin.org/get')
done()
}) })
it('does basic get request with redirects (303)', async () => { it('does basic get request with redirects (303)', async done => {
const res: httpm.HttpClientResponse = await _http.get( let res: httpm.HttpClientResponse = await _http.get(
`https://httpbin.org/redirect-to?url=${encodeURIComponent( 'https://httpbin.org/redirect-to?url=' +
'https://httpbin.org/get' encodeURIComponent('https://httpbin.org/get') +
)}&status_code=303` '&status_code=303'
) )
expect(res.message.statusCode).toBe(200) expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody() let body: string = await res.readBody()
const obj = JSON.parse(body) let obj: any = JSON.parse(body)
expect(obj.url).toBe('https://httpbin.org/get') expect(obj.url).toBe('https://httpbin.org/get')
done()
}) })
it('returns 404 for not found get request on redirect', async () => { it('returns 404 for not found get request on redirect', async done => {
const res: httpm.HttpClientResponse = await _http.get( let res: httpm.HttpClientResponse = await _http.get(
`https://httpbin.org/redirect-to?url=${encodeURIComponent( 'https://httpbin.org/redirect-to?url=' +
'https://httpbin.org/status/404' encodeURIComponent('https://httpbin.org/status/404') +
)}&status_code=303` '&status_code=303'
) )
expect(res.message.statusCode).toBe(404) expect(res.message.statusCode).toBe(404)
await res.readBody() let body: string = await res.readBody()
done()
}) })
it('does not follow redirects if disabled', async () => { it('does not follow redirects if disabled', async done => {
const http: httpm.HttpClient = new httpm.HttpClient( let http: httpm.HttpClient = new httpm.HttpClient(
'typed-test-client-tests', 'typed-test-client-tests',
undefined, null,
{allowRedirects: false} {allowRedirects: false}
) )
const res: httpm.HttpClientResponse = await http.get( let res: httpm.HttpClientResponse = await http.get(
`https://httpbin.org/redirect-to?url=${encodeURIComponent( 'https://httpbin.org/redirect-to?url=' +
'https://httpbin.org/get' encodeURIComponent('https://httpbin.org/get')
)}`
) )
expect(res.message.statusCode).toBe(302) expect(res.message.statusCode).toBe(302)
await res.readBody() let body: string = await res.readBody()
done()
}) })
it('does not pass auth with diff hostname redirects', async () => { it('does not pass auth with diff hostname redirects', async done => {
const headers = { let headers = {
accept: 'application/json', accept: 'application/json',
authorization: 'shhh' authorization: 'shhh'
} }
const res: httpm.HttpClientResponse = await _http.get( let res: httpm.HttpClientResponse = await _http.get(
`https://httpbin.org/redirect-to?url=${encodeURIComponent( 'https://httpbin.org/redirect-to?url=' +
'https://www.httpbin.org/get' encodeURIComponent('https://www.httpbin.org/get'),
)}`,
headers headers
) )
expect(res.message.statusCode).toBe(200) expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody() let body: string = await res.readBody()
const obj = JSON.parse(body) let obj: any = JSON.parse(body)
// httpbin "fixes" the casing // httpbin "fixes" the casing
expect(obj.headers['Accept']).toBe('application/json') expect(obj.headers['Accept']).toBe('application/json')
expect(obj.headers['Authorization']).toBeUndefined() expect(obj.headers['Authorization']).toBeUndefined()
expect(obj.headers['authorization']).toBeUndefined() expect(obj.headers['authorization']).toBeUndefined()
expect(obj.url).toBe('https://www.httpbin.org/get') expect(obj.url).toBe('https://www.httpbin.org/get')
done()
}) })
it('does not pass Auth with diff hostname redirects', async () => { it('does not pass Auth with diff hostname redirects', async done => {
const headers = { let headers = {
Accept: 'application/json', Accept: 'application/json',
Authorization: 'shhh' Authorization: 'shhh'
} }
const res: httpm.HttpClientResponse = await _http.get( let res: httpm.HttpClientResponse = await _http.get(
`https://httpbin.org/redirect-to?url=${encodeURIComponent( 'https://httpbin.org/redirect-to?url=' +
'https://www.httpbin.org/get' encodeURIComponent('https://www.httpbin.org/get'),
)}`,
headers headers
) )
expect(res.message.statusCode).toBe(200) expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody() let body: string = await res.readBody()
const obj = JSON.parse(body) let obj: any = JSON.parse(body)
// httpbin "fixes" the casing // httpbin "fixes" the casing
expect(obj.headers['Accept']).toBe('application/json') expect(obj.headers['Accept']).toBe('application/json')
expect(obj.headers['Authorization']).toBeUndefined() expect(obj.headers['Authorization']).toBeUndefined()
expect(obj.headers['authorization']).toBeUndefined() expect(obj.headers['authorization']).toBeUndefined()
expect(obj.url).toBe('https://www.httpbin.org/get') expect(obj.url).toBe('https://www.httpbin.org/get')
done()
}) })
it('does basic head request', async () => { it('does basic head request', async done => {
const res: httpm.HttpClientResponse = await _http.head( let res: httpm.HttpClientResponse = await _http.head(
'http://httpbin.org/get' 'http://httpbin.org/get'
) )
expect(res.message.statusCode).toBe(200) expect(res.message.statusCode).toBe(200)
done()
}) })
it('does basic http delete request', async () => { it('does basic http delete request', async done => {
const res: httpm.HttpClientResponse = await _http.del( let res: httpm.HttpClientResponse = await _http.del(
'http://httpbin.org/delete' 'http://httpbin.org/delete'
) )
expect(res.message.statusCode).toBe(200) expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody() let body: string = await res.readBody()
JSON.parse(body) let obj: any = JSON.parse(body)
done()
}) })
it('does basic http post request', async () => { it('does basic http post request', async done => {
const b = 'Hello World!' let b: string = 'Hello World!'
const res: httpm.HttpClientResponse = await _http.post( let res: httpm.HttpClientResponse = await _http.post(
'http://httpbin.org/post', 'http://httpbin.org/post',
b b
) )
expect(res.message.statusCode).toBe(200) expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody() let body: string = await res.readBody()
const obj = JSON.parse(body) let obj: any = JSON.parse(body)
expect(obj.data).toBe(b) expect(obj.data).toBe(b)
expect(obj.url).toBe('http://httpbin.org/post') expect(obj.url).toBe('http://httpbin.org/post')
done()
}) })
it('does basic http patch request', async () => { it('does basic http patch request', async done => {
const b = 'Hello World!' let b: string = 'Hello World!'
const res: httpm.HttpClientResponse = await _http.patch( let res: httpm.HttpClientResponse = await _http.patch(
'http://httpbin.org/patch', 'http://httpbin.org/patch',
b b
) )
expect(res.message.statusCode).toBe(200) expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody() let body: string = await res.readBody()
const obj = JSON.parse(body) let obj: any = JSON.parse(body)
expect(obj.data).toBe(b) expect(obj.data).toBe(b)
expect(obj.url).toBe('http://httpbin.org/patch') expect(obj.url).toBe('http://httpbin.org/patch')
done()
}) })
it('does basic http options request', async () => { it('does basic http options request', async done => {
const res: httpm.HttpClientResponse = await _http.options( let res: httpm.HttpClientResponse = await _http.options(
'http://httpbin.org' 'http://httpbin.org'
) )
expect(res.message.statusCode).toBe(200) expect(res.message.statusCode).toBe(200)
await res.readBody() let body: string = await res.readBody()
done()
}) })
it('returns 404 for not found get request', async () => { it('returns 404 for not found get request', async done => {
const res: httpm.HttpClientResponse = await _http.get( let res: httpm.HttpClientResponse = await _http.get(
'http://httpbin.org/status/404' 'http://httpbin.org/status/404'
) )
expect(res.message.statusCode).toBe(404) expect(res.message.statusCode).toBe(404)
await res.readBody() let body: string = await res.readBody()
done()
}) })
it('gets a json object', async () => { it('gets a json object', async () => {
const jsonObj = await _http.getJson<HttpBinData>('https://httpbin.org/get') let jsonObj: ifm.ITypedResponse<HttpBinData> = await _http.getJson<
HttpBinData
>('https://httpbin.org/get')
expect(jsonObj.statusCode).toBe(200) expect(jsonObj.statusCode).toBe(200)
expect(jsonObj.result).toBeDefined() expect(jsonObj.result).toBeDefined()
expect(jsonObj.result?.url).toBe('https://httpbin.org/get') expect(jsonObj.result.url).toBe('https://httpbin.org/get')
expect(jsonObj.result?.headers['Accept']).toBe( expect(jsonObj.result.headers['Accept']).toBe(
httpm.MediaTypes.ApplicationJson httpm.MediaTypes.ApplicationJson
) )
expect(jsonObj.headers[httpm.Headers.ContentType]).toBe( expect(jsonObj.headers[httpm.Headers.ContentType]).toBe(
@@ -301,27 +305,26 @@ describe('basics', () => {
}) })
it('getting a non existent json object returns null', async () => { it('getting a non existent json object returns null', async () => {
const jsonObj = await _http.getJson<HttpBinData>( let jsonObj: ifm.ITypedResponse<HttpBinData> = await _http.getJson<
'https://httpbin.org/status/404' HttpBinData
) >('https://httpbin.org/status/404')
expect(jsonObj.statusCode).toBe(404) expect(jsonObj.statusCode).toBe(404)
expect(jsonObj.result).toBeNull() expect(jsonObj.result).toBeNull()
}) })
it('posts a json object', async () => { it('posts a json object', async () => {
const res = {name: 'foo'} let res: any = {name: 'foo'}
const restRes = await _http.postJson<HttpBinData>( let restRes: ifm.ITypedResponse<HttpBinData> = await _http.postJson<
'https://httpbin.org/post', HttpBinData
res >('https://httpbin.org/post', res)
)
expect(restRes.statusCode).toBe(200) expect(restRes.statusCode).toBe(200)
expect(restRes.result).toBeDefined() expect(restRes.result).toBeDefined()
expect(restRes.result?.url).toBe('https://httpbin.org/post') expect(restRes.result.url).toBe('https://httpbin.org/post')
expect(restRes.result?.json.name).toBe('foo') expect(restRes.result.json.name).toBe('foo')
expect(restRes.result?.headers['Accept']).toBe( expect(restRes.result.headers['Accept']).toBe(
httpm.MediaTypes.ApplicationJson httpm.MediaTypes.ApplicationJson
) )
expect(restRes.result?.headers['Content-Type']).toBe( expect(restRes.result.headers['Content-Type']).toBe(
httpm.MediaTypes.ApplicationJson httpm.MediaTypes.ApplicationJson
) )
expect(restRes.headers[httpm.Headers.ContentType]).toBe( expect(restRes.headers[httpm.Headers.ContentType]).toBe(
@@ -330,20 +333,19 @@ describe('basics', () => {
}) })
it('puts a json object', async () => { it('puts a json object', async () => {
const res = {name: 'foo'} let res: any = {name: 'foo'}
const restRes = await _http.putJson<HttpBinData>( let restRes: ifm.ITypedResponse<HttpBinData> = await _http.putJson<
'https://httpbin.org/put', HttpBinData
res >('https://httpbin.org/put', res)
)
expect(restRes.statusCode).toBe(200) expect(restRes.statusCode).toBe(200)
expect(restRes.result).toBeDefined() expect(restRes.result).toBeDefined()
expect(restRes.result?.url).toBe('https://httpbin.org/put') expect(restRes.result.url).toBe('https://httpbin.org/put')
expect(restRes.result?.json.name).toBe('foo') expect(restRes.result.json.name).toBe('foo')
expect(restRes.result?.headers['Accept']).toBe( expect(restRes.result.headers['Accept']).toBe(
httpm.MediaTypes.ApplicationJson httpm.MediaTypes.ApplicationJson
) )
expect(restRes.result?.headers['Content-Type']).toBe( expect(restRes.result.headers['Content-Type']).toBe(
httpm.MediaTypes.ApplicationJson httpm.MediaTypes.ApplicationJson
) )
expect(restRes.headers[httpm.Headers.ContentType]).toBe( expect(restRes.headers[httpm.Headers.ContentType]).toBe(
@@ -352,19 +354,18 @@ describe('basics', () => {
}) })
it('patch a json object', async () => { it('patch a json object', async () => {
const res = {name: 'foo'} let res: any = {name: 'foo'}
const restRes = await _http.patchJson<HttpBinData>( let restRes: ifm.ITypedResponse<HttpBinData> = await _http.patchJson<
'https://httpbin.org/patch', HttpBinData
res >('https://httpbin.org/patch', res)
)
expect(restRes.statusCode).toBe(200) expect(restRes.statusCode).toBe(200)
expect(restRes.result).toBeDefined() expect(restRes.result).toBeDefined()
expect(restRes.result?.url).toBe('https://httpbin.org/patch') expect(restRes.result.url).toBe('https://httpbin.org/patch')
expect(restRes.result?.json.name).toBe('foo') expect(restRes.result.json.name).toBe('foo')
expect(restRes.result?.headers['Accept']).toBe( expect(restRes.result.headers['Accept']).toBe(
httpm.MediaTypes.ApplicationJson httpm.MediaTypes.ApplicationJson
) )
expect(restRes.result?.headers['Content-Type']).toBe( expect(restRes.result.headers['Content-Type']).toBe(
httpm.MediaTypes.ApplicationJson httpm.MediaTypes.ApplicationJson
) )
expect(restRes.headers[httpm.Headers.ContentType]).toBe( expect(restRes.headers[httpm.Headers.ContentType]).toBe(
+14 -15
View File
@@ -1,6 +1,5 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ import * as httpm from '../_out'
import * as ifm from '../_out/interfaces'
import * as httpm from '..'
describe('headers', () => { describe('headers', () => {
let _http: httpm.HttpClient let _http: httpm.HttpClient
@@ -10,8 +9,8 @@ describe('headers', () => {
}) })
it('preserves existing headers on getJson', async () => { it('preserves existing headers on getJson', async () => {
const additionalHeaders = {[httpm.Headers.Accept]: 'foo'} let additionalHeaders = {[httpm.Headers.Accept]: 'foo'}
let jsonObj = await _http.getJson<any>( let jsonObj: ifm.ITypedResponse<any> = await _http.getJson<any>(
'https://httpbin.org/get', 'https://httpbin.org/get',
additionalHeaders additionalHeaders
) )
@@ -20,7 +19,7 @@ describe('headers', () => {
httpm.MediaTypes.ApplicationJson httpm.MediaTypes.ApplicationJson
) )
const httpWithHeaders = new httpm.HttpClient() let httpWithHeaders = new httpm.HttpClient()
httpWithHeaders.requestOptions = { httpWithHeaders.requestOptions = {
headers: { headers: {
[httpm.Headers.Accept]: 'baz' [httpm.Headers.Accept]: 'baz'
@@ -34,8 +33,8 @@ describe('headers', () => {
}) })
it('preserves existing headers on postJson', async () => { it('preserves existing headers on postJson', async () => {
const additionalHeaders = {[httpm.Headers.Accept]: 'foo'} let additionalHeaders = {[httpm.Headers.Accept]: 'foo'}
let jsonObj = await _http.postJson<any>( let jsonObj: ifm.ITypedResponse<any> = await _http.postJson<any>(
'https://httpbin.org/post', 'https://httpbin.org/post',
{}, {},
additionalHeaders additionalHeaders
@@ -45,7 +44,7 @@ describe('headers', () => {
httpm.MediaTypes.ApplicationJson httpm.MediaTypes.ApplicationJson
) )
const httpWithHeaders = new httpm.HttpClient() let httpWithHeaders = new httpm.HttpClient()
httpWithHeaders.requestOptions = { httpWithHeaders.requestOptions = {
headers: { headers: {
[httpm.Headers.Accept]: 'baz' [httpm.Headers.Accept]: 'baz'
@@ -62,8 +61,8 @@ describe('headers', () => {
}) })
it('preserves existing headers on putJson', async () => { it('preserves existing headers on putJson', async () => {
const additionalHeaders = {[httpm.Headers.Accept]: 'foo'} let additionalHeaders = {[httpm.Headers.Accept]: 'foo'}
let jsonObj = await _http.putJson<any>( let jsonObj: ifm.ITypedResponse<any> = await _http.putJson<any>(
'https://httpbin.org/put', 'https://httpbin.org/put',
{}, {},
additionalHeaders additionalHeaders
@@ -73,7 +72,7 @@ describe('headers', () => {
httpm.MediaTypes.ApplicationJson httpm.MediaTypes.ApplicationJson
) )
const httpWithHeaders = new httpm.HttpClient() let httpWithHeaders = new httpm.HttpClient()
httpWithHeaders.requestOptions = { httpWithHeaders.requestOptions = {
headers: { headers: {
[httpm.Headers.Accept]: 'baz' [httpm.Headers.Accept]: 'baz'
@@ -87,8 +86,8 @@ describe('headers', () => {
}) })
it('preserves existing headers on patchJson', async () => { it('preserves existing headers on patchJson', async () => {
const additionalHeaders = {[httpm.Headers.Accept]: 'foo'} let additionalHeaders = {[httpm.Headers.Accept]: 'foo'}
let jsonObj = await _http.patchJson<any>( let jsonObj: ifm.ITypedResponse<any> = await _http.patchJson<any>(
'https://httpbin.org/patch', 'https://httpbin.org/patch',
{}, {},
additionalHeaders additionalHeaders
@@ -98,7 +97,7 @@ describe('headers', () => {
httpm.MediaTypes.ApplicationJson httpm.MediaTypes.ApplicationJson
) )
const httpWithHeaders = new httpm.HttpClient() let httpWithHeaders = new httpm.HttpClient()
httpWithHeaders.requestOptions = { httpWithHeaders.requestOptions = {
headers: { headers: {
[httpm.Headers.Accept]: 'baz' [httpm.Headers.Accept]: 'baz'
@@ -1,4 +1,4 @@
import * as httpm from '../lib' import * as httpm from '../_out'
describe('basics', () => { describe('basics', () => {
let _http: httpm.HttpClient let _http: httpm.HttpClient
@@ -11,63 +11,69 @@ describe('basics', () => {
_http.dispose() _http.dispose()
}) })
it('does basic http get request with keepAlive true', async () => { it('does basic http get request with keepAlive true', async done => {
const res: httpm.HttpClientResponse = await _http.get( let res: httpm.HttpClientResponse = await _http.get(
'http://httpbin.org/get' 'http://httpbin.org/get'
) )
expect(res.message.statusCode).toBe(200) expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody() let body: string = await res.readBody()
const obj = JSON.parse(body) let obj: any = JSON.parse(body)
expect(obj.url).toBe('http://httpbin.org/get') expect(obj.url).toBe('http://httpbin.org/get')
done()
}) })
it('does basic head request with keepAlive true', async () => { it('does basic head request with keepAlive true', async done => {
const res: httpm.HttpClientResponse = await _http.head( let res: httpm.HttpClientResponse = await _http.head(
'http://httpbin.org/get' 'http://httpbin.org/get'
) )
expect(res.message.statusCode).toBe(200) expect(res.message.statusCode).toBe(200)
done()
}) })
it('does basic http delete request with keepAlive true', async () => { it('does basic http delete request with keepAlive true', async done => {
const res: httpm.HttpClientResponse = await _http.del( let res: httpm.HttpClientResponse = await _http.del(
'http://httpbin.org/delete' 'http://httpbin.org/delete'
) )
expect(res.message.statusCode).toBe(200) expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody() let body: string = await res.readBody()
JSON.parse(body) let obj: any = JSON.parse(body)
done()
}) })
it('does basic http post request with keepAlive true', async () => { it('does basic http post request with keepAlive true', async done => {
const b = 'Hello World!' let b: string = 'Hello World!'
const res: httpm.HttpClientResponse = await _http.post( let res: httpm.HttpClientResponse = await _http.post(
'http://httpbin.org/post', 'http://httpbin.org/post',
b b
) )
expect(res.message.statusCode).toBe(200) expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody() let body: string = await res.readBody()
const obj = JSON.parse(body) let obj: any = JSON.parse(body)
expect(obj.data).toBe(b) expect(obj.data).toBe(b)
expect(obj.url).toBe('http://httpbin.org/post') expect(obj.url).toBe('http://httpbin.org/post')
done()
}) })
it('does basic http patch request with keepAlive true', async () => { it('does basic http patch request with keepAlive true', async done => {
const b = 'Hello World!' let b: string = 'Hello World!'
const res: httpm.HttpClientResponse = await _http.patch( let res: httpm.HttpClientResponse = await _http.patch(
'http://httpbin.org/patch', 'http://httpbin.org/patch',
b b
) )
expect(res.message.statusCode).toBe(200) expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody() let body: string = await res.readBody()
const obj = JSON.parse(body) let obj: any = JSON.parse(body)
expect(obj.data).toBe(b) expect(obj.data).toBe(b)
expect(obj.url).toBe('http://httpbin.org/patch') expect(obj.url).toBe('http://httpbin.org/patch')
done()
}) })
it('does basic http options request with keepAlive true', async () => { it('does basic http options request with keepAlive true', async done => {
const res: httpm.HttpClientResponse = await _http.options( let res: httpm.HttpClientResponse = await _http.options(
'http://httpbin.org' 'http://httpbin.org'
) )
expect(res.message.statusCode).toBe(200) expect(res.message.statusCode).toBe(200)
await res.readBody() let body: string = await res.readBody()
done()
}) })
}) })
+39 -43
View File
@@ -1,20 +1,18 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import * as http from 'http' import * as http from 'http'
import * as httpm from '../lib/' import * as httpm from '../_out'
import * as pm from '../lib/proxy' import * as pm from '../_out/proxy'
// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports import * as proxy from 'proxy'
const proxy = require('proxy') import * as tunnelm from 'tunnel'
let _proxyConnects: string[] let _proxyConnects: string[]
let _proxyServer: http.Server let _proxyServer: http.Server
const _proxyUrl = 'http://127.0.0.1:8080' let _proxyUrl = 'http://127.0.0.1:8080'
describe('proxy', () => { describe('proxy', () => {
beforeAll(async () => { beforeAll(async () => {
// Start proxy server // Start proxy server
_proxyServer = proxy() _proxyServer = proxy()
await new Promise<void>(resolve => { await new Promise(resolve => {
const port = Number(_proxyUrl.split(':')[2]) const port = Number(_proxyUrl.split(':')[2])
_proxyServer.listen(port, () => resolve()) _proxyServer.listen(port, () => resolve())
}) })
@@ -34,126 +32,126 @@ describe('proxy', () => {
_clearVars() _clearVars()
// Stop proxy server // Stop proxy server
await new Promise<void>(resolve => { await new Promise(resolve => {
_proxyServer.once('close', () => resolve()) _proxyServer.once('close', () => resolve())
_proxyServer.close() _proxyServer.close()
}) })
}) })
it('getProxyUrl does not return proxyUrl if variables not set', () => { it('getProxyUrl does not return proxyUrl if variables not set', () => {
const proxyUrl = pm.getProxyUrl(new URL('https://github.com')) let proxyUrl = pm.getProxyUrl(new URL('https://github.com'))
expect(proxyUrl).toBeUndefined() expect(proxyUrl).toBeUndefined()
}) })
it('getProxyUrl returns proxyUrl if https_proxy set for https url', () => { it('getProxyUrl returns proxyUrl if https_proxy set for https url', () => {
process.env['https_proxy'] = 'https://myproxysvr' process.env['https_proxy'] = 'https://myproxysvr'
const proxyUrl = pm.getProxyUrl(new URL('https://github.com')) let proxyUrl = pm.getProxyUrl(new URL('https://github.com'))
expect(proxyUrl).toBeDefined() expect(proxyUrl).toBeDefined()
}) })
it('getProxyUrl does not return proxyUrl if http_proxy set for https url', () => { it('getProxyUrl does not return proxyUrl if http_proxy set for https url', () => {
process.env['http_proxy'] = 'https://myproxysvr' process.env['http_proxy'] = 'https://myproxysvr'
const proxyUrl = pm.getProxyUrl(new URL('https://github.com')) let proxyUrl = pm.getProxyUrl(new URL('https://github.com'))
expect(proxyUrl).toBeUndefined() expect(proxyUrl).toBeUndefined()
}) })
it('getProxyUrl returns proxyUrl if http_proxy set for http url', () => { it('getProxyUrl returns proxyUrl if http_proxy set for http url', () => {
process.env['http_proxy'] = 'http://myproxysvr' process.env['http_proxy'] = 'http://myproxysvr'
const proxyUrl = pm.getProxyUrl(new URL('http://github.com')) let proxyUrl = pm.getProxyUrl(new URL('http://github.com'))
expect(proxyUrl).toBeDefined() expect(proxyUrl).toBeDefined()
}) })
it('getProxyUrl does not return proxyUrl if https_proxy set and in no_proxy list', () => { it('getProxyUrl does not return proxyUrl if https_proxy set and in no_proxy list', () => {
process.env['https_proxy'] = 'https://myproxysvr' process.env['https_proxy'] = 'https://myproxysvr'
process.env['no_proxy'] = 'otherserver,myserver,anotherserver:8080' process.env['no_proxy'] = 'otherserver,myserver,anotherserver:8080'
const proxyUrl = pm.getProxyUrl(new URL('https://myserver')) let proxyUrl = pm.getProxyUrl(new URL('https://myserver'))
expect(proxyUrl).toBeUndefined() expect(proxyUrl).toBeUndefined()
}) })
it('getProxyUrl returns proxyUrl if https_proxy set and not in no_proxy list', () => { it('getProxyUrl returns proxyUrl if https_proxy set and not in no_proxy list', () => {
process.env['https_proxy'] = 'https://myproxysvr' process.env['https_proxy'] = 'https://myproxysvr'
process.env['no_proxy'] = 'otherserver,myserver,anotherserver:8080' process.env['no_proxy'] = 'otherserver,myserver,anotherserver:8080'
const proxyUrl = pm.getProxyUrl(new URL('https://github.com')) let proxyUrl = pm.getProxyUrl(new URL('https://github.com'))
expect(proxyUrl).toBeDefined() expect(proxyUrl).toBeDefined()
}) })
it('getProxyUrl does not return proxyUrl if http_proxy set and in no_proxy list', () => { it('getProxyUrl does not return proxyUrl if http_proxy set and in no_proxy list', () => {
process.env['http_proxy'] = 'http://myproxysvr' process.env['http_proxy'] = 'http://myproxysvr'
process.env['no_proxy'] = 'otherserver,myserver,anotherserver:8080' process.env['no_proxy'] = 'otherserver,myserver,anotherserver:8080'
const proxyUrl = pm.getProxyUrl(new URL('http://myserver')) let proxyUrl = pm.getProxyUrl(new URL('http://myserver'))
expect(proxyUrl).toBeUndefined() expect(proxyUrl).toBeUndefined()
}) })
it('getProxyUrl returns proxyUrl if http_proxy set and not in no_proxy list', () => { it('getProxyUrl returns proxyUrl if http_proxy set and not in no_proxy list', () => {
process.env['http_proxy'] = 'http://myproxysvr' process.env['http_proxy'] = 'http://myproxysvr'
process.env['no_proxy'] = 'otherserver,myserver,anotherserver:8080' process.env['no_proxy'] = 'otherserver,myserver,anotherserver:8080'
const proxyUrl = pm.getProxyUrl(new URL('http://github.com')) let proxyUrl = pm.getProxyUrl(new URL('http://github.com'))
expect(proxyUrl).toBeDefined() expect(proxyUrl).toBeDefined()
}) })
it('checkBypass returns true if host as no_proxy list', () => { it('checkBypass returns true if host as no_proxy list', () => {
process.env['no_proxy'] = 'myserver' process.env['no_proxy'] = 'myserver'
const bypass = pm.checkBypass(new URL('https://myserver')) let bypass = pm.checkBypass(new URL('https://myserver'))
expect(bypass).toBeTruthy() expect(bypass).toBeTruthy()
}) })
it('checkBypass returns true if host in no_proxy list', () => { it('checkBypass returns true if host in no_proxy list', () => {
process.env['no_proxy'] = 'otherserver,myserver,anotherserver:8080' process.env['no_proxy'] = 'otherserver,myserver,anotherserver:8080'
const bypass = pm.checkBypass(new URL('https://myserver')) let bypass = pm.checkBypass(new URL('https://myserver'))
expect(bypass).toBeTruthy() expect(bypass).toBeTruthy()
}) })
it('checkBypass returns true if host in no_proxy list with spaces', () => { it('checkBypass returns true if host in no_proxy list with spaces', () => {
process.env['no_proxy'] = 'otherserver, myserver ,anotherserver:8080' process.env['no_proxy'] = 'otherserver, myserver ,anotherserver:8080'
const bypass = pm.checkBypass(new URL('https://myserver')) let bypass = pm.checkBypass(new URL('https://myserver'))
expect(bypass).toBeTruthy() expect(bypass).toBeTruthy()
}) })
it('checkBypass returns true if host in no_proxy list with port', () => { it('checkBypass returns true if host in no_proxy list with port', () => {
process.env['no_proxy'] = 'otherserver, myserver:8080 ,anotherserver' process.env['no_proxy'] = 'otherserver, myserver:8080 ,anotherserver'
const bypass = pm.checkBypass(new URL('https://myserver:8080')) let bypass = pm.checkBypass(new URL('https://myserver:8080'))
expect(bypass).toBeTruthy() expect(bypass).toBeTruthy()
}) })
it('checkBypass returns true if host with port in no_proxy list without port', () => { it('checkBypass returns true if host with port in no_proxy list without port', () => {
process.env['no_proxy'] = 'otherserver, myserver ,anotherserver' process.env['no_proxy'] = 'otherserver, myserver ,anotherserver'
const bypass = pm.checkBypass(new URL('https://myserver:8080')) let bypass = pm.checkBypass(new URL('https://myserver:8080'))
expect(bypass).toBeTruthy() expect(bypass).toBeTruthy()
}) })
it('checkBypass returns true if host in no_proxy list with default https port', () => { it('checkBypass returns true if host in no_proxy list with default https port', () => {
process.env['no_proxy'] = 'otherserver, myserver:443 ,anotherserver' process.env['no_proxy'] = 'otherserver, myserver:443 ,anotherserver'
const bypass = pm.checkBypass(new URL('https://myserver')) let bypass = pm.checkBypass(new URL('https://myserver'))
expect(bypass).toBeTruthy() expect(bypass).toBeTruthy()
}) })
it('checkBypass returns true if host in no_proxy list with default http port', () => { it('checkBypass returns true if host in no_proxy list with default http port', () => {
process.env['no_proxy'] = 'otherserver, myserver:80 ,anotherserver' process.env['no_proxy'] = 'otherserver, myserver:80 ,anotherserver'
const bypass = pm.checkBypass(new URL('http://myserver')) let bypass = pm.checkBypass(new URL('http://myserver'))
expect(bypass).toBeTruthy() expect(bypass).toBeTruthy()
}) })
it('checkBypass returns false if host not in no_proxy list', () => { it('checkBypass returns false if host not in no_proxy list', () => {
process.env['no_proxy'] = 'otherserver, myserver ,anotherserver:8080' process.env['no_proxy'] = 'otherserver, myserver ,anotherserver:8080'
const bypass = pm.checkBypass(new URL('https://github.com')) let bypass = pm.checkBypass(new URL('https://github.com'))
expect(bypass).toBeFalsy() expect(bypass).toBeFalsy()
}) })
it('checkBypass returns false if empty no_proxy', () => { it('checkBypass returns false if empty no_proxy', () => {
process.env['no_proxy'] = '' process.env['no_proxy'] = ''
const bypass = pm.checkBypass(new URL('https://github.com')) let bypass = pm.checkBypass(new URL('https://github.com'))
expect(bypass).toBeFalsy() expect(bypass).toBeFalsy()
}) })
it('HttpClient does basic http get request through proxy', async () => { it('HttpClient does basic http get request through proxy', async () => {
process.env['http_proxy'] = _proxyUrl process.env['http_proxy'] = _proxyUrl
const httpClient = new httpm.HttpClient() const httpClient = new httpm.HttpClient()
const res: httpm.HttpClientResponse = await httpClient.get( let res: httpm.HttpClientResponse = await httpClient.get(
'http://httpbin.org/get' 'http://httpbin.org/get'
) )
expect(res.message.statusCode).toBe(200) expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody() let body: string = await res.readBody()
const obj = JSON.parse(body) let obj: any = JSON.parse(body)
expect(obj.url).toBe('http://httpbin.org/get') expect(obj.url).toBe('http://httpbin.org/get')
expect(_proxyConnects).toEqual(['httpbin.org:80']) expect(_proxyConnects).toEqual(['httpbin.org:80'])
}) })
@@ -162,12 +160,12 @@ describe('proxy', () => {
process.env['http_proxy'] = _proxyUrl process.env['http_proxy'] = _proxyUrl
process.env['no_proxy'] = 'httpbin.org' process.env['no_proxy'] = 'httpbin.org'
const httpClient = new httpm.HttpClient() const httpClient = new httpm.HttpClient()
const res: httpm.HttpClientResponse = await httpClient.get( let res: httpm.HttpClientResponse = await httpClient.get(
'http://httpbin.org/get' 'http://httpbin.org/get'
) )
expect(res.message.statusCode).toBe(200) expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody() let body: string = await res.readBody()
const obj = JSON.parse(body) let obj: any = JSON.parse(body)
expect(obj.url).toBe('http://httpbin.org/get') expect(obj.url).toBe('http://httpbin.org/get')
expect(_proxyConnects).toHaveLength(0) expect(_proxyConnects).toHaveLength(0)
}) })
@@ -175,12 +173,12 @@ describe('proxy', () => {
it('HttpClient does basic https get request through proxy', async () => { it('HttpClient does basic https get request through proxy', async () => {
process.env['https_proxy'] = _proxyUrl process.env['https_proxy'] = _proxyUrl
const httpClient = new httpm.HttpClient() const httpClient = new httpm.HttpClient()
const res: httpm.HttpClientResponse = await httpClient.get( let res: httpm.HttpClientResponse = await httpClient.get(
'https://httpbin.org/get' 'https://httpbin.org/get'
) )
expect(res.message.statusCode).toBe(200) expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody() let body: string = await res.readBody()
const obj = JSON.parse(body) let obj: any = JSON.parse(body)
expect(obj.url).toBe('https://httpbin.org/get') expect(obj.url).toBe('https://httpbin.org/get')
expect(_proxyConnects).toEqual(['httpbin.org:443']) expect(_proxyConnects).toEqual(['httpbin.org:443'])
}) })
@@ -189,12 +187,12 @@ describe('proxy', () => {
process.env['https_proxy'] = _proxyUrl process.env['https_proxy'] = _proxyUrl
process.env['no_proxy'] = 'httpbin.org' process.env['no_proxy'] = 'httpbin.org'
const httpClient = new httpm.HttpClient() const httpClient = new httpm.HttpClient()
const res: httpm.HttpClientResponse = await httpClient.get( let res: httpm.HttpClientResponse = await httpClient.get(
'https://httpbin.org/get' 'https://httpbin.org/get'
) )
expect(res.message.statusCode).toBe(200) expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody() let body: string = await res.readBody()
const obj = JSON.parse(body) let obj: any = JSON.parse(body)
expect(obj.url).toBe('https://httpbin.org/get') expect(obj.url).toBe('https://httpbin.org/get')
expect(_proxyConnects).toHaveLength(0) expect(_proxyConnects).toHaveLength(0)
}) })
@@ -202,8 +200,7 @@ describe('proxy', () => {
it('proxyAuth not set in tunnel agent when authentication is not provided', async () => { it('proxyAuth not set in tunnel agent when authentication is not provided', async () => {
process.env['https_proxy'] = 'http://127.0.0.1:8080' process.env['https_proxy'] = 'http://127.0.0.1:8080'
const httpClient = new httpm.HttpClient() const httpClient = new httpm.HttpClient()
const agent: any = httpClient.getAgent('https://some-url') let agent: tunnelm.TunnelingAgent = httpClient.getAgent('https://some-url')
// eslint-disable-next-line no-console
console.log(agent) console.log(agent)
expect(agent.proxyOptions.host).toBe('127.0.0.1') expect(agent.proxyOptions.host).toBe('127.0.0.1')
expect(agent.proxyOptions.port).toBe('8080') expect(agent.proxyOptions.port).toBe('8080')
@@ -213,8 +210,7 @@ describe('proxy', () => {
it('proxyAuth is set in tunnel agent when authentication is provided', async () => { it('proxyAuth is set in tunnel agent when authentication is provided', async () => {
process.env['https_proxy'] = 'http://user:[email protected]:8080' process.env['https_proxy'] = 'http://user:[email protected]:8080'
const httpClient = new httpm.HttpClient() const httpClient = new httpm.HttpClient()
const agent: any = httpClient.getAgent('https://some-url') let agent: tunnelm.TunnelingAgent = httpClient.getAgent('https://some-url')
// eslint-disable-next-line no-console
console.log(agent) console.log(agent)
expect(agent.proxyOptions.host).toBe('127.0.0.1') expect(agent.proxyOptions.host).toBe('127.0.0.1')
expect(agent.proxyOptions.port).toBe('8080') expect(agent.proxyOptions.port).toBe('8080')
@@ -222,7 +218,7 @@ describe('proxy', () => {
}) })
}) })
function _clearVars(): void { function _clearVars() {
delete process.env.http_proxy delete process.env.http_proxy
delete process.env.HTTP_PROXY delete process.env.HTTP_PROXY
delete process.env.https_proxy delete process.env.https_proxy
+10289 -127
View File
File diff suppressed because it is too large Load Diff
+25 -32
View File
@@ -1,46 +1,39 @@
{ {
"name": "@actions/http-client", "name": "@actions/http-client",
"version": "2.0.0", "version": "1.0.11",
"description": "Actions Http Client", "description": "Actions Http Client",
"keywords": [ "main": "index.js",
"github", "scripts": {
"actions", "build": "rm -Rf ./_out && tsc && cp package*.json ./_out && cp *.md ./_out && cp LICENSE ./_out && cp actions.png ./_out",
"http" "test": "jest",
], "format": "prettier --write *.ts && prettier --write **/*.ts",
"homepage": "https://github.com/actions/toolkit/tree/main/packages/http-client", "format-check": "prettier --check *.ts && prettier --check **/*.ts",
"license": "MIT", "audit-check": "npm audit --audit-level=moderate"
"main": "lib/index.js",
"types": "lib/index.d.ts",
"directories": {
"lib": "lib",
"test": "__tests__"
},
"files": [
"lib",
"!.DS_Store"
],
"publishConfig": {
"access": "public"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/actions/toolkit.git", "url": "git+https://github.com/actions/http-client.git"
"directory": "packages/github"
},
"scripts": {
"audit-moderate": "npm install && npm audit --json --audit-level=moderate > audit.json",
"test": "echo \"Error: run tests from root\" && exit 1",
"build": "tsc",
"format": "prettier --write **/*.ts",
"format-check": "prettier --check **/*.ts",
"tsc": "tsc"
}, },
"keywords": [
"Actions",
"Http"
],
"author": "GitHub, Inc.",
"license": "MIT",
"bugs": { "bugs": {
"url": "https://github.com/actions/toolkit/issues" "url": "https://github.com/actions/http-client/issues"
}, },
"homepage": "https://github.com/actions/http-client#readme",
"devDependencies": { "devDependencies": {
"@types/tunnel": "0.0.3", "@types/jest": "^25.1.4",
"@types/node": "^12.12.31",
"jest": "^25.1.0",
"prettier": "^2.0.4",
"proxy": "^1.0.1", "proxy": "^1.0.1",
"ts-jest": "^25.2.1",
"typescript": "^3.8.3"
},
"dependencies": {
"tunnel": "0.0.6" "tunnel": "0.0.6"
} }
} }
+34 -34
View File
@@ -1,8 +1,6 @@
import * as http from 'http' import ifm = require('./interfaces')
import * as ifm from './interfaces'
import {HttpClientResponse} from './index'
export class BasicCredentialHandler implements ifm.RequestHandler { export class BasicCredentialHandler implements ifm.IRequestHandler {
username: string username: string
password: string password: string
@@ -11,26 +9,27 @@ export class BasicCredentialHandler implements ifm.RequestHandler {
this.password = password this.password = password
} }
prepareRequest(options: http.RequestOptions): void { prepareRequest(options: any): void {
if (!options.headers) { options.headers['Authorization'] =
throw Error('The request has no headers') 'Basic ' +
} Buffer.from(this.username + ':' + this.password).toString('base64')
options.headers['Authorization'] = `Basic ${Buffer.from(
`${this.username}:${this.password}`
).toString('base64')}`
} }
// This handler cannot handle 401 // This handler cannot handle 401
canHandleAuthentication(): boolean { canHandleAuthentication(response: ifm.IHttpClientResponse): boolean {
return false return false
} }
async handleAuthentication(): Promise<HttpClientResponse> { handleAuthentication(
throw new Error('not implemented') httpClient: ifm.IHttpClient,
requestInfo: ifm.IRequestInfo,
objs
): Promise<ifm.IHttpClientResponse> {
return null
} }
} }
export class BearerCredentialHandler implements ifm.RequestHandler { export class BearerCredentialHandler implements ifm.IRequestHandler {
token: string token: string
constructor(token: string) { constructor(token: string) {
@@ -39,25 +38,26 @@ export class BearerCredentialHandler implements ifm.RequestHandler {
// currently implements pre-authorization // currently implements pre-authorization
// TODO: support preAuth = false where it hooks on 401 // TODO: support preAuth = false where it hooks on 401
prepareRequest(options: http.RequestOptions): void { prepareRequest(options: any): void {
if (!options.headers) { options.headers['Authorization'] = 'Bearer ' + this.token
throw Error('The request has no headers')
}
options.headers['Authorization'] = `Bearer ${this.token}`
} }
// This handler cannot handle 401 // This handler cannot handle 401
canHandleAuthentication(): boolean { canHandleAuthentication(response: ifm.IHttpClientResponse): boolean {
return false return false
} }
async handleAuthentication(): Promise<HttpClientResponse> { handleAuthentication(
throw new Error('not implemented') httpClient: ifm.IHttpClient,
requestInfo: ifm.IRequestInfo,
objs
): Promise<ifm.IHttpClientResponse> {
return null
} }
} }
export class PersonalAccessTokenCredentialHandler export class PersonalAccessTokenCredentialHandler
implements ifm.RequestHandler { implements ifm.IRequestHandler {
token: string token: string
constructor(token: string) { constructor(token: string) {
@@ -66,21 +66,21 @@ export class PersonalAccessTokenCredentialHandler
// currently implements pre-authorization // currently implements pre-authorization
// TODO: support preAuth = false where it hooks on 401 // TODO: support preAuth = false where it hooks on 401
prepareRequest(options: http.RequestOptions): void { prepareRequest(options: any): void {
if (!options.headers) { options.headers['Authorization'] =
throw Error('The request has no headers') 'Basic ' + Buffer.from('PAT:' + this.token).toString('base64')
}
options.headers['Authorization'] = `Basic ${Buffer.from(
`PAT:${this.token}`
).toString('base64')}`
} }
// This handler cannot handle 401 // This handler cannot handle 401
canHandleAuthentication(): boolean { canHandleAuthentication(response: ifm.IHttpClientResponse): boolean {
return false return false
} }
async handleAuthentication(): Promise<HttpClientResponse> { handleAuthentication(
throw new Error('not implemented') httpClient: ifm.IHttpClient,
requestInfo: ifm.IRequestInfo,
objs
): Promise<ifm.IHttpClientResponse> {
return null
} }
} }
+178 -183
View File
@@ -1,11 +1,9 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ import http = require('http')
import https = require('https')
import ifm = require('./interfaces')
import pm = require('./proxy')
import * as http from 'http' let tunnel: any
import * as https from 'https'
import * as ifm from './interfaces'
import * as net from 'net'
import * as pm from './proxy'
import * as tunnel from 'tunnel'
export enum HttpCodes { export enum HttpCodes {
OK = 200, OK = 200,
@@ -51,7 +49,7 @@ export enum MediaTypes {
* @param serverUrl The server URL where the request will be sent. For example, https://api.github.com * @param serverUrl The server URL where the request will be sent. For example, https://api.github.com
*/ */
export function getProxyUrl(serverUrl: string): string { export function getProxyUrl(serverUrl: string): string {
const proxyUrl = pm.getProxyUrl(new URL(serverUrl)) let proxyUrl = pm.getProxyUrl(new URL(serverUrl))
return proxyUrl ? proxyUrl.href : '' return proxyUrl ? proxyUrl.href : ''
} }
@@ -79,18 +77,18 @@ export class HttpClientError extends Error {
Object.setPrototypeOf(this, HttpClientError.prototype) Object.setPrototypeOf(this, HttpClientError.prototype)
} }
statusCode: number public statusCode: number
result?: any public result?: any
} }
export class HttpClientResponse { export class HttpClientResponse implements ifm.IHttpClientResponse {
constructor(message: http.IncomingMessage) { constructor(message: http.IncomingMessage) {
this.message = message this.message = message
} }
message: http.IncomingMessage public message: http.IncomingMessage
async readBody(): Promise<string> { readBody(): Promise<string> {
return new Promise<string>(async resolve => { return new Promise<string>(async (resolve, reject) => {
let output = Buffer.alloc(0) let output = Buffer.alloc(0)
this.message.on('data', (chunk: Buffer) => { this.message.on('data', (chunk: Buffer) => {
@@ -104,32 +102,32 @@ export class HttpClientResponse {
} }
} }
export function isHttps(requestUrl: string): boolean { export function isHttps(requestUrl: string) {
const parsedUrl: URL = new URL(requestUrl) let parsedUrl: URL = new URL(requestUrl)
return parsedUrl.protocol === 'https:' return parsedUrl.protocol === 'https:'
} }
export class HttpClient { export class HttpClient {
userAgent: string | undefined userAgent: string | undefined
handlers: ifm.RequestHandler[] handlers: ifm.IRequestHandler[]
requestOptions: ifm.RequestOptions | undefined requestOptions: ifm.IRequestOptions
private _ignoreSslError = false private _ignoreSslError: boolean = false
private _socketTimeout: number | undefined private _socketTimeout: number
private _allowRedirects = true private _allowRedirects: boolean = true
private _allowRedirectDowngrade = false private _allowRedirectDowngrade: boolean = false
private _maxRedirects = 50 private _maxRedirects: number = 50
private _allowRetries = false private _allowRetries: boolean = false
private _maxRetries = 1 private _maxRetries: number = 1
private _agent: any private _agent
private _proxyAgent: any private _proxyAgent
private _keepAlive = false private _keepAlive: boolean = false
private _disposed = false private _disposed: boolean = false
constructor( constructor(
userAgent?: string, userAgent?: string,
handlers?: ifm.RequestHandler[], handlers?: ifm.IRequestHandler[],
requestOptions?: ifm.RequestOptions requestOptions?: ifm.IRequestOptions
) { ) {
this.userAgent = userAgent this.userAgent = userAgent
this.handlers = handlers || [] this.handlers = handlers || []
@@ -167,64 +165,64 @@ export class HttpClient {
} }
} }
async options( public options(
requestUrl: string, requestUrl: string,
additionalHeaders?: http.OutgoingHttpHeaders additionalHeaders?: ifm.IHeaders
): Promise<HttpClientResponse> { ): Promise<ifm.IHttpClientResponse> {
return this.request('OPTIONS', requestUrl, null, additionalHeaders || {}) return this.request('OPTIONS', requestUrl, null, additionalHeaders || {})
} }
async get( public get(
requestUrl: string, requestUrl: string,
additionalHeaders?: http.OutgoingHttpHeaders additionalHeaders?: ifm.IHeaders
): Promise<HttpClientResponse> { ): Promise<ifm.IHttpClientResponse> {
return this.request('GET', requestUrl, null, additionalHeaders || {}) return this.request('GET', requestUrl, null, additionalHeaders || {})
} }
async del( public del(
requestUrl: string, requestUrl: string,
additionalHeaders?: http.OutgoingHttpHeaders additionalHeaders?: ifm.IHeaders
): Promise<HttpClientResponse> { ): Promise<ifm.IHttpClientResponse> {
return this.request('DELETE', requestUrl, null, additionalHeaders || {}) return this.request('DELETE', requestUrl, null, additionalHeaders || {})
} }
async post( public post(
requestUrl: string, requestUrl: string,
data: string, data: string,
additionalHeaders?: http.OutgoingHttpHeaders additionalHeaders?: ifm.IHeaders
): Promise<HttpClientResponse> { ): Promise<ifm.IHttpClientResponse> {
return this.request('POST', requestUrl, data, additionalHeaders || {}) return this.request('POST', requestUrl, data, additionalHeaders || {})
} }
async patch( public patch(
requestUrl: string, requestUrl: string,
data: string, data: string,
additionalHeaders?: http.OutgoingHttpHeaders additionalHeaders?: ifm.IHeaders
): Promise<HttpClientResponse> { ): Promise<ifm.IHttpClientResponse> {
return this.request('PATCH', requestUrl, data, additionalHeaders || {}) return this.request('PATCH', requestUrl, data, additionalHeaders || {})
} }
async put( public put(
requestUrl: string, requestUrl: string,
data: string, data: string,
additionalHeaders?: http.OutgoingHttpHeaders additionalHeaders?: ifm.IHeaders
): Promise<HttpClientResponse> { ): Promise<ifm.IHttpClientResponse> {
return this.request('PUT', requestUrl, data, additionalHeaders || {}) return this.request('PUT', requestUrl, data, additionalHeaders || {})
} }
async head( public head(
requestUrl: string, requestUrl: string,
additionalHeaders?: http.OutgoingHttpHeaders additionalHeaders?: ifm.IHeaders
): Promise<HttpClientResponse> { ): Promise<ifm.IHttpClientResponse> {
return this.request('HEAD', requestUrl, null, additionalHeaders || {}) return this.request('HEAD', requestUrl, null, additionalHeaders || {})
} }
async sendStream( public sendStream(
verb: string, verb: string,
requestUrl: string, requestUrl: string,
stream: NodeJS.ReadableStream, stream: NodeJS.ReadableStream,
additionalHeaders?: http.OutgoingHttpHeaders additionalHeaders?: ifm.IHeaders
): Promise<HttpClientResponse> { ): Promise<ifm.IHttpClientResponse> {
return this.request(verb, requestUrl, stream, additionalHeaders) return this.request(verb, requestUrl, stream, additionalHeaders)
} }
@@ -232,28 +230,28 @@ export class HttpClient {
* Gets a typed object from an endpoint * Gets a typed object from an endpoint
* Be aware that not found returns a null. Other errors (4xx, 5xx) reject the promise * Be aware that not found returns a null. Other errors (4xx, 5xx) reject the promise
*/ */
async getJson<T>( public async getJson<T>(
requestUrl: string, requestUrl: string,
additionalHeaders: http.OutgoingHttpHeaders = {} additionalHeaders: ifm.IHeaders = {}
): Promise<ifm.TypedResponse<T>> { ): Promise<ifm.ITypedResponse<T>> {
additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader( additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(
additionalHeaders, additionalHeaders,
Headers.Accept, Headers.Accept,
MediaTypes.ApplicationJson MediaTypes.ApplicationJson
) )
const res: HttpClientResponse = await this.get( let res: ifm.IHttpClientResponse = await this.get(
requestUrl, requestUrl,
additionalHeaders additionalHeaders
) )
return this._processResponse<T>(res, this.requestOptions) return this._processResponse<T>(res, this.requestOptions)
} }
async postJson<T>( public async postJson<T>(
requestUrl: string, requestUrl: string,
obj: any, obj: any,
additionalHeaders: http.OutgoingHttpHeaders = {} additionalHeaders: ifm.IHeaders = {}
): Promise<ifm.TypedResponse<T>> { ): Promise<ifm.ITypedResponse<T>> {
const data: string = JSON.stringify(obj, null, 2) let data: string = JSON.stringify(obj, null, 2)
additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader( additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(
additionalHeaders, additionalHeaders,
Headers.Accept, Headers.Accept,
@@ -264,7 +262,7 @@ export class HttpClient {
Headers.ContentType, Headers.ContentType,
MediaTypes.ApplicationJson MediaTypes.ApplicationJson
) )
const res: HttpClientResponse = await this.post( let res: ifm.IHttpClientResponse = await this.post(
requestUrl, requestUrl,
data, data,
additionalHeaders additionalHeaders
@@ -272,12 +270,12 @@ export class HttpClient {
return this._processResponse<T>(res, this.requestOptions) return this._processResponse<T>(res, this.requestOptions)
} }
async putJson<T>( public async putJson<T>(
requestUrl: string, requestUrl: string,
obj: any, obj: any,
additionalHeaders: http.OutgoingHttpHeaders = {} additionalHeaders: ifm.IHeaders = {}
): Promise<ifm.TypedResponse<T>> { ): Promise<ifm.ITypedResponse<T>> {
const data: string = JSON.stringify(obj, null, 2) let data: string = JSON.stringify(obj, null, 2)
additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader( additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(
additionalHeaders, additionalHeaders,
Headers.Accept, Headers.Accept,
@@ -288,7 +286,7 @@ export class HttpClient {
Headers.ContentType, Headers.ContentType,
MediaTypes.ApplicationJson MediaTypes.ApplicationJson
) )
const res: HttpClientResponse = await this.put( let res: ifm.IHttpClientResponse = await this.put(
requestUrl, requestUrl,
data, data,
additionalHeaders additionalHeaders
@@ -296,12 +294,12 @@ export class HttpClient {
return this._processResponse<T>(res, this.requestOptions) return this._processResponse<T>(res, this.requestOptions)
} }
async patchJson<T>( public async patchJson<T>(
requestUrl: string, requestUrl: string,
obj: any, obj: any,
additionalHeaders: http.OutgoingHttpHeaders = {} additionalHeaders: ifm.IHeaders = {}
): Promise<ifm.TypedResponse<T>> { ): Promise<ifm.ITypedResponse<T>> {
const data: string = JSON.stringify(obj, null, 2) let data: string = JSON.stringify(obj, null, 2)
additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader( additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(
additionalHeaders, additionalHeaders,
Headers.Accept, Headers.Accept,
@@ -312,7 +310,7 @@ export class HttpClient {
Headers.ContentType, Headers.ContentType,
MediaTypes.ApplicationJson MediaTypes.ApplicationJson
) )
const res: HttpClientResponse = await this.patch( let res: ifm.IHttpClientResponse = await this.patch(
requestUrl, requestUrl,
data, data,
additionalHeaders additionalHeaders
@@ -325,28 +323,28 @@ export class HttpClient {
* All other methods such as get, post, patch, and request ultimately call this. * All other methods such as get, post, patch, and request ultimately call this.
* Prefer get, del, post and patch * Prefer get, del, post and patch
*/ */
async request( public async request(
verb: string, verb: string,
requestUrl: string, requestUrl: string,
data: string | NodeJS.ReadableStream | null, data: string | NodeJS.ReadableStream,
headers?: http.OutgoingHttpHeaders headers: ifm.IHeaders
): Promise<HttpClientResponse> { ): Promise<ifm.IHttpClientResponse> {
if (this._disposed) { if (this._disposed) {
throw new Error('Client has already been disposed.') throw new Error('Client has already been disposed.')
} }
const parsedUrl = new URL(requestUrl) let parsedUrl = new URL(requestUrl)
let info: ifm.RequestInfo = this._prepareRequest(verb, parsedUrl, headers) let info: ifm.IRequestInfo = this._prepareRequest(verb, parsedUrl, headers)
// Only perform retries on reads since writes may not be idempotent. // Only perform retries on reads since writes may not be idempotent.
const maxTries: number = let maxTries: number =
this._allowRetries && RetryableHttpVerbs.includes(verb) this._allowRetries && RetryableHttpVerbs.indexOf(verb) != -1
? this._maxRetries + 1 ? this._maxRetries + 1
: 1 : 1
let numTries = 0 let numTries: number = 0
let response: HttpClientResponse | undefined let response: HttpClientResponse
do { while (numTries < maxTries) {
response = await this.requestRaw(info, data) response = await this.requestRaw(info, data)
// Check if it's an authentication challenge // Check if it's an authentication challenge
@@ -355,11 +353,11 @@ export class HttpClient {
response.message && response.message &&
response.message.statusCode === HttpCodes.Unauthorized response.message.statusCode === HttpCodes.Unauthorized
) { ) {
let authenticationHandler: ifm.RequestHandler | undefined let authenticationHandler: ifm.IRequestHandler
for (const handler of this.handlers) { for (let i = 0; i < this.handlers.length; i++) {
if (handler.canHandleAuthentication(response)) { if (this.handlers[i].canHandleAuthentication(response)) {
authenticationHandler = handler authenticationHandler = this.handlers[i]
break break
} }
} }
@@ -375,21 +373,19 @@ export class HttpClient {
let redirectsRemaining: number = this._maxRedirects let redirectsRemaining: number = this._maxRedirects
while ( while (
response.message.statusCode && HttpRedirectCodes.indexOf(response.message.statusCode) != -1 &&
HttpRedirectCodes.includes(response.message.statusCode) &&
this._allowRedirects && this._allowRedirects &&
redirectsRemaining > 0 redirectsRemaining > 0
) { ) {
const redirectUrl: string | undefined = const redirectUrl: string | null = response.message.headers['location']
response.message.headers['location']
if (!redirectUrl) { if (!redirectUrl) {
// if there's no location to redirect to, we won't // if there's no location to redirect to, we won't
break break
} }
const parsedRedirectUrl = new URL(redirectUrl) let parsedRedirectUrl = new URL(redirectUrl)
if ( if (
parsedUrl.protocol === 'https:' && parsedUrl.protocol == 'https:' &&
parsedUrl.protocol !== parsedRedirectUrl.protocol && parsedUrl.protocol != parsedRedirectUrl.protocol &&
!this._allowRedirectDowngrade !this._allowRedirectDowngrade
) { ) {
throw new Error( throw new Error(
@@ -403,7 +399,7 @@ export class HttpClient {
// strip authorization header if redirected to a different hostname // strip authorization header if redirected to a different hostname
if (parsedRedirectUrl.hostname !== parsedUrl.hostname) { if (parsedRedirectUrl.hostname !== parsedUrl.hostname) {
for (const header in headers) { for (let header in headers) {
// header names are case insensitive // header names are case insensitive
if (header.toLowerCase() === 'authorization') { if (header.toLowerCase() === 'authorization') {
delete headers[header] delete headers[header]
@@ -417,10 +413,7 @@ export class HttpClient {
redirectsRemaining-- redirectsRemaining--
} }
if ( if (HttpResponseRetryCodes.indexOf(response.message.statusCode) == -1) {
!response.message.statusCode ||
!HttpResponseRetryCodes.includes(response.message.statusCode)
) {
// If not a retry code, return immediately instead of retrying // If not a retry code, return immediately instead of retrying
return response return response
} }
@@ -431,7 +424,7 @@ export class HttpClient {
await response.readBody() await response.readBody()
await this._performExponentialBackoff(numTries) await this._performExponentialBackoff(numTries)
} }
} while (numTries < maxTries) }
return response return response
} }
@@ -439,7 +432,7 @@ export class HttpClient {
/** /**
* Needs to be called if keepAlive is set to true in request options. * Needs to be called if keepAlive is set to true in request options.
*/ */
dispose(): void { public dispose() {
if (this._agent) { if (this._agent) {
this._agent.destroy() this._agent.destroy()
} }
@@ -452,20 +445,20 @@ export class HttpClient {
* @param info * @param info
* @param data * @param data
*/ */
async requestRaw( public requestRaw(
info: ifm.RequestInfo, info: ifm.IRequestInfo,
data: string | NodeJS.ReadableStream | null data: string | NodeJS.ReadableStream
): Promise<HttpClientResponse> { ): Promise<ifm.IHttpClientResponse> {
return new Promise<HttpClientResponse>((resolve, reject) => { return new Promise<ifm.IHttpClientResponse>((resolve, reject) => {
function callbackForResult(err?: Error, res?: HttpClientResponse): void { let callbackForResult = function (
err: any,
res: ifm.IHttpClientResponse
) {
if (err) { if (err) {
reject(err) reject(err)
} else if (!res) {
// If `err` is not passed, then `res` must be passed.
reject(new Error('Unknown error'))
} else {
resolve(res)
} }
resolve(res)
} }
this.requestRawWithCallback(info, data, callbackForResult) this.requestRawWithCallback(info, data, callbackForResult)
@@ -478,35 +471,33 @@ export class HttpClient {
* @param data * @param data
* @param onResult * @param onResult
*/ */
requestRawWithCallback( public requestRawWithCallback(
info: ifm.RequestInfo, info: ifm.IRequestInfo,
data: string | NodeJS.ReadableStream | null, data: string | NodeJS.ReadableStream,
onResult: (err?: Error, res?: HttpClientResponse) => void onResult: (err: any, res: ifm.IHttpClientResponse) => void
): void { ): void {
let socket
if (typeof data === 'string') { if (typeof data === 'string') {
if (!info.options.headers) {
info.options.headers = {}
}
info.options.headers['Content-Length'] = Buffer.byteLength(data, 'utf8') info.options.headers['Content-Length'] = Buffer.byteLength(data, 'utf8')
} }
let callbackCalled = false let callbackCalled: boolean = false
function handleResult(err?: Error, res?: HttpClientResponse): void { let handleResult = (err: any, res: HttpClientResponse) => {
if (!callbackCalled) { if (!callbackCalled) {
callbackCalled = true callbackCalled = true
onResult(err, res) onResult(err, res)
} }
} }
const req: http.ClientRequest = info.httpModule.request( let req: http.ClientRequest = info.httpModule.request(
info.options, info.options,
(msg: http.IncomingMessage) => { (msg: http.IncomingMessage) => {
const res: HttpClientResponse = new HttpClientResponse(msg) let res: HttpClientResponse = new HttpClientResponse(msg)
handleResult(undefined, res) handleResult(null, res)
} }
) )
let socket: net.Socket
req.on('socket', sock => { req.on('socket', sock => {
socket = sock socket = sock
}) })
@@ -516,13 +507,13 @@ export class HttpClient {
if (socket) { if (socket) {
socket.end() socket.end()
} }
handleResult(new Error(`Request timeout: ${info.options.path}`)) handleResult(new Error('Request timeout: ' + info.options.path), null)
}) })
req.on('error', function(err) { req.on('error', function (err) {
// err has statusCode property // err has statusCode property
// res should have headers // res should have headers
handleResult(err) handleResult(err, null)
}) })
if (data && typeof data === 'string') { if (data && typeof data === 'string') {
@@ -530,7 +521,7 @@ export class HttpClient {
} }
if (data && typeof data !== 'string') { if (data && typeof data !== 'string') {
data.on('close', function() { data.on('close', function () {
req.end() req.end()
}) })
@@ -545,17 +536,17 @@ export class HttpClient {
* routing through a proxy server - depending upon the url and proxy environment variables. * routing through a proxy server - depending upon the url and proxy environment variables.
* @param serverUrl The server URL where the request will be sent. For example, https://api.github.com * @param serverUrl The server URL where the request will be sent. For example, https://api.github.com
*/ */
getAgent(serverUrl: string): http.Agent { public getAgent(serverUrl: string): http.Agent {
const parsedUrl = new URL(serverUrl) let parsedUrl = new URL(serverUrl)
return this._getAgent(parsedUrl) return this._getAgent(parsedUrl)
} }
private _prepareRequest( private _prepareRequest(
method: string, method: string,
requestUrl: URL, requestUrl: URL,
headers?: http.OutgoingHttpHeaders headers: ifm.IHeaders
): ifm.RequestInfo { ): ifm.IRequestInfo {
const info: ifm.RequestInfo = <ifm.RequestInfo>{} const info: ifm.IRequestInfo = <ifm.IRequestInfo>{}
info.parsedUrl = requestUrl info.parsedUrl = requestUrl
const usingSsl: boolean = info.parsedUrl.protocol === 'https:' const usingSsl: boolean = info.parsedUrl.protocol === 'https:'
@@ -579,22 +570,23 @@ export class HttpClient {
// gives handlers an opportunity to participate // gives handlers an opportunity to participate
if (this.handlers) { if (this.handlers) {
for (const handler of this.handlers) { this.handlers.forEach(handler => {
handler.prepareRequest(info.options) handler.prepareRequest(info.options)
} })
} }
return info return info
} }
private _mergeHeaders( private _mergeHeaders(headers: ifm.IHeaders): ifm.IHeaders {
headers?: http.OutgoingHttpHeaders const lowercaseKeys = obj =>
): http.OutgoingHttpHeaders { Object.keys(obj).reduce((c, k) => ((c[k.toLowerCase()] = obj[k]), c), {})
if (this.requestOptions && this.requestOptions.headers) { if (this.requestOptions && this.requestOptions.headers) {
return Object.assign( return Object.assign(
{}, {},
lowercaseKeys(this.requestOptions.headers), lowercaseKeys(this.requestOptions.headers),
lowercaseKeys(headers || {}) lowercaseKeys(headers)
) )
} }
@@ -602,11 +594,14 @@ export class HttpClient {
} }
private _getExistingOrDefaultHeader( private _getExistingOrDefaultHeader(
additionalHeaders: http.OutgoingHttpHeaders, additionalHeaders: ifm.IHeaders,
header: string, header: string,
_default: string _default: string
): string | number | string[] { ) {
let clientHeader: string | undefined const lowercaseKeys = obj =>
Object.keys(obj).reduce((c, k) => ((c[k.toLowerCase()] = obj[k]), c), {})
let clientHeader: string
if (this.requestOptions && this.requestOptions.headers) { if (this.requestOptions && this.requestOptions.headers) {
clientHeader = lowercaseKeys(this.requestOptions.headers)[header] clientHeader = lowercaseKeys(this.requestOptions.headers)[header]
} }
@@ -615,8 +610,8 @@ export class HttpClient {
private _getAgent(parsedUrl: URL): http.Agent { private _getAgent(parsedUrl: URL): http.Agent {
let agent let agent
const proxyUrl = pm.getProxyUrl(parsedUrl) let proxyUrl: URL = pm.getProxyUrl(parsedUrl)
const useProxy = proxyUrl && proxyUrl.hostname let useProxy = proxyUrl && proxyUrl.hostname
if (this._keepAlive && useProxy) { if (this._keepAlive && useProxy) {
agent = this._proxyAgent agent = this._proxyAgent
@@ -627,20 +622,24 @@ export class HttpClient {
} }
// if agent is already assigned use that agent. // if agent is already assigned use that agent.
if (agent) { if (!!agent) {
return agent return agent
} }
const usingSsl = parsedUrl.protocol === 'https:' const usingSsl = parsedUrl.protocol === 'https:'
let maxSockets = 100 let maxSockets = 100
if (this.requestOptions) { if (!!this.requestOptions) {
maxSockets = this.requestOptions.maxSockets || http.globalAgent.maxSockets maxSockets = this.requestOptions.maxSockets || http.globalAgent.maxSockets
} }
// This is `useProxy` again, but we need to check `proxyURl` directly for TypeScripts's flow analysis. if (useProxy) {
if (proxyUrl && proxyUrl.hostname) { // If using proxy, need tunnel
if (!tunnel) {
tunnel = require('tunnel')
}
const agentOptions = { const agentOptions = {
maxSockets, maxSockets: maxSockets,
keepAlive: this._keepAlive, keepAlive: this._keepAlive,
proxy: { proxy: {
...((proxyUrl.username || proxyUrl.password) && { ...((proxyUrl.username || proxyUrl.password) && {
@@ -665,7 +664,7 @@ export class HttpClient {
// if reusing agent across request and tunneling agent isn't assigned create a new agent // if reusing agent across request and tunneling agent isn't assigned create a new agent
if (this._keepAlive && !agent) { if (this._keepAlive && !agent) {
const options = {keepAlive: this._keepAlive, maxSockets} const options = {keepAlive: this._keepAlive, maxSockets: maxSockets}
agent = usingSsl ? new https.Agent(options) : new http.Agent(options) agent = usingSsl ? new https.Agent(options) : new http.Agent(options)
this._agent = agent this._agent = agent
} }
@@ -687,51 +686,50 @@ export class HttpClient {
return agent return agent
} }
private async _performExponentialBackoff(retryNumber: number): Promise<void> { private _performExponentialBackoff(retryNumber: number): Promise<void> {
retryNumber = Math.min(ExponentialBackoffCeiling, retryNumber) retryNumber = Math.min(ExponentialBackoffCeiling, retryNumber)
const ms: number = ExponentialBackoffTimeSlice * Math.pow(2, retryNumber) const ms: number = ExponentialBackoffTimeSlice * Math.pow(2, retryNumber)
return new Promise(resolve => setTimeout(() => resolve(), ms)) return new Promise(resolve => setTimeout(() => resolve(), ms))
} }
private async _processResponse<T>( private static dateTimeDeserializer(key: any, value: any): any {
res: HttpClientResponse, if (typeof value === 'string') {
options?: ifm.RequestOptions let a = new Date(value)
): Promise<ifm.TypedResponse<T>> { if (!isNaN(a.valueOf())) {
return new Promise<ifm.TypedResponse<T>>(async (resolve, reject) => { return a
const statusCode = res.message.statusCode || 0 }
}
const response: ifm.TypedResponse<T> = { return value
statusCode, }
private async _processResponse<T>(
res: ifm.IHttpClientResponse,
options: ifm.IRequestOptions
): Promise<ifm.ITypedResponse<T>> {
return new Promise<ifm.ITypedResponse<T>>(async (resolve, reject) => {
const statusCode: number = res.message.statusCode
const response: ifm.ITypedResponse<T> = {
statusCode: statusCode,
result: null, result: null,
headers: {} headers: {}
} }
// not found leads to null obj returned // not found leads to null obj returned
if (statusCode === HttpCodes.NotFound) { if (statusCode == HttpCodes.NotFound) {
resolve(response) resolve(response)
} }
// get the result from the body
function dateTimeDeserializer(key: any, value: any): any {
if (typeof value === 'string') {
const a = new Date(value)
if (!isNaN(a.valueOf())) {
return a
}
}
return value
}
let obj: any let obj: any
let contents: string | undefined let contents: string
// get the result from the body
try { try {
contents = await res.readBody() contents = await res.readBody()
if (contents && contents.length > 0) { if (contents && contents.length > 0) {
if (options && options.deserializeDates) { if (options && options.deserializeDates) {
obj = JSON.parse(contents, dateTimeDeserializer) obj = JSON.parse(contents, HttpClient.dateTimeDeserializer)
} else { } else {
obj = JSON.parse(contents) obj = JSON.parse(contents)
} }
@@ -755,10 +753,10 @@ export class HttpClient {
// it may be the case that the exception is in the body message as string // it may be the case that the exception is in the body message as string
msg = contents msg = contents
} else { } else {
msg = `Failed request: (${statusCode})` msg = 'Failed request: (' + statusCode + ')'
} }
const err = new HttpClientError(msg, statusCode) let err = new HttpClientError(msg, statusCode)
err.result = response.result err.result = response.result
reject(err) reject(err)
@@ -768,6 +766,3 @@ export class HttpClient {
}) })
} }
} }
const lowercaseKeys = (obj: {[index: string]: any}): any =>
Object.keys(obj).reduce((c: any, k) => ((c[k.toLowerCase()] = obj[k]), c), {})
+43 -36
View File
@@ -1,76 +1,83 @@
import * as http from 'http' import http = require('http')
import * as https from 'https'
import {HttpClientResponse} from './index'
export interface HttpClient { export interface IHeaders {
[key: string]: any
}
export interface IHttpClient {
options( options(
requestUrl: string, requestUrl: string,
additionalHeaders?: http.OutgoingHttpHeaders additionalHeaders?: IHeaders
): Promise<HttpClientResponse> ): Promise<IHttpClientResponse>
get( get(
requestUrl: string, requestUrl: string,
additionalHeaders?: http.OutgoingHttpHeaders additionalHeaders?: IHeaders
): Promise<HttpClientResponse> ): Promise<IHttpClientResponse>
del( del(
requestUrl: string, requestUrl: string,
additionalHeaders?: http.OutgoingHttpHeaders additionalHeaders?: IHeaders
): Promise<HttpClientResponse> ): Promise<IHttpClientResponse>
post( post(
requestUrl: string, requestUrl: string,
data: string, data: string,
additionalHeaders?: http.OutgoingHttpHeaders additionalHeaders?: IHeaders
): Promise<HttpClientResponse> ): Promise<IHttpClientResponse>
patch( patch(
requestUrl: string, requestUrl: string,
data: string, data: string,
additionalHeaders?: http.OutgoingHttpHeaders additionalHeaders?: IHeaders
): Promise<HttpClientResponse> ): Promise<IHttpClientResponse>
put( put(
requestUrl: string, requestUrl: string,
data: string, data: string,
additionalHeaders?: http.OutgoingHttpHeaders additionalHeaders?: IHeaders
): Promise<HttpClientResponse> ): Promise<IHttpClientResponse>
sendStream( sendStream(
verb: string, verb: string,
requestUrl: string, requestUrl: string,
stream: NodeJS.ReadableStream, stream: NodeJS.ReadableStream,
additionalHeaders?: http.OutgoingHttpHeaders additionalHeaders?: IHeaders
): Promise<HttpClientResponse> ): Promise<IHttpClientResponse>
request( request(
verb: string, verb: string,
requestUrl: string, requestUrl: string,
data: string | NodeJS.ReadableStream, data: string | NodeJS.ReadableStream,
headers: http.OutgoingHttpHeaders headers: IHeaders
): Promise<HttpClientResponse> ): Promise<IHttpClientResponse>
requestRaw( requestRaw(
info: RequestInfo, info: IRequestInfo,
data: string | NodeJS.ReadableStream data: string | NodeJS.ReadableStream
): Promise<HttpClientResponse> ): Promise<IHttpClientResponse>
requestRawWithCallback( requestRawWithCallback(
info: RequestInfo, info: IRequestInfo,
data: string | NodeJS.ReadableStream, data: string | NodeJS.ReadableStream,
onResult: (err?: Error, res?: HttpClientResponse) => void onResult: (err: any, res: IHttpClientResponse) => void
): void ): void
} }
export interface RequestHandler { export interface IRequestHandler {
prepareRequest(options: http.RequestOptions): void prepareRequest(options: http.RequestOptions): void
canHandleAuthentication(response: HttpClientResponse): boolean canHandleAuthentication(response: IHttpClientResponse): boolean
handleAuthentication( handleAuthentication(
httpClient: HttpClient, httpClient: IHttpClient,
requestInfo: RequestInfo, requestInfo: IRequestInfo,
data: string | NodeJS.ReadableStream | null objs
): Promise<HttpClientResponse> ): Promise<IHttpClientResponse>
} }
export interface RequestInfo { export interface IHttpClientResponse {
message: http.IncomingMessage
readBody(): Promise<string>
}
export interface IRequestInfo {
options: http.RequestOptions options: http.RequestOptions
parsedUrl: URL parsedUrl: URL
httpModule: typeof http | typeof https httpModule: any
} }
export interface RequestOptions { export interface IRequestOptions {
headers?: http.OutgoingHttpHeaders headers?: IHeaders
socketTimeout?: number socketTimeout?: number
ignoreSslError?: boolean ignoreSslError?: boolean
allowRedirects?: boolean allowRedirects?: boolean
@@ -84,8 +91,8 @@ export interface RequestOptions {
maxRetries?: number maxRetries?: number
} }
export interface TypedResponse<T> { export interface ITypedResponse<T> {
statusCode: number statusCode: number
result: T | null result: T | null
headers: http.IncomingHttpHeaders headers: Object
} }
+16 -16
View File
@@ -1,23 +1,23 @@
export function getProxyUrl(reqUrl: URL): URL | undefined { export function getProxyUrl(reqUrl: URL): URL | undefined {
const usingSsl = reqUrl.protocol === 'https:' let usingSsl = reqUrl.protocol === 'https:'
let proxyUrl: URL
if (checkBypass(reqUrl)) { if (checkBypass(reqUrl)) {
return undefined return proxyUrl
} }
const proxyVar = (() => { let proxyVar: string
if (usingSsl) { if (usingSsl) {
return process.env['https_proxy'] || process.env['HTTPS_PROXY'] proxyVar = process.env['https_proxy'] || process.env['HTTPS_PROXY']
} else { } else {
return process.env['http_proxy'] || process.env['HTTP_PROXY'] proxyVar = process.env['http_proxy'] || process.env['HTTP_PROXY']
} }
})()
if (proxyVar) { if (proxyVar) {
return new URL(proxyVar) proxyUrl = new URL(proxyVar)
} else {
return undefined
} }
return proxyUrl
} }
export function checkBypass(reqUrl: URL): boolean { export function checkBypass(reqUrl: URL): boolean {
@@ -25,13 +25,13 @@ export function checkBypass(reqUrl: URL): boolean {
return false return false
} }
const noProxy = process.env['no_proxy'] || process.env['NO_PROXY'] || '' let noProxy = process.env['no_proxy'] || process.env['NO_PROXY'] || ''
if (!noProxy) { if (!noProxy) {
return false return false
} }
// Determine the request port // Determine the request port
let reqPort: number | undefined let reqPort: number
if (reqUrl.port) { if (reqUrl.port) {
reqPort = Number(reqUrl.port) reqPort = Number(reqUrl.port)
} else if (reqUrl.protocol === 'http:') { } else if (reqUrl.protocol === 'http:') {
@@ -41,13 +41,13 @@ export function checkBypass(reqUrl: URL): boolean {
} }
// Format the request hostname and hostname with port // Format the request hostname and hostname with port
const upperReqHosts = [reqUrl.hostname.toUpperCase()] let upperReqHosts = [reqUrl.hostname.toUpperCase()]
if (typeof reqPort === 'number') { if (typeof reqPort === 'number') {
upperReqHosts.push(`${upperReqHosts[0]}:${reqPort}`) upperReqHosts.push(`${upperReqHosts[0]}:${reqPort}`)
} }
// Compare request host against noproxy // Compare request host against noproxy
for (const upperNoProxyItem of noProxy for (let upperNoProxyItem of noProxy
.split(',') .split(',')
.map(x => x.trim().toUpperCase()) .map(x => x.trim().toUpperCase())
.filter(x => x)) { .filter(x => x)) {
-11
View File
@@ -1,11 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./lib",
"rootDir": "./src",
"moduleResolution": "node"
},
"include": [
"./src"
]
}
+2 -2
View File
@@ -9,5 +9,5 @@ if [[ -z "$name" ]]; then
exit 1 exit 1
fi fi
npx lerna create @actions/$name lerna create @actions/$name
cp packages/core/tsconfig.json packages/$name/tsconfig.json cp packages/toolkit/tsconfig.json packages/$name/tsconfig.json