Skip to content

Commit

Permalink
move from github.com/gofika/util/httputil
Browse files Browse the repository at this point in the history
  • Loading branch information
leaker committed Jan 31, 2024
1 parent a7bb366 commit ee35e2d
Show file tree
Hide file tree
Showing 12 changed files with 638 additions and 0 deletions.
21 changes: 21 additions & 0 deletions .github/workflows/auto-merge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Dependabot auto-merge
on: pull_request_target
permissions:
pull-requests: write
contents: write
jobs:
dependabot:
runs-on: ubuntu-latest
if: ${{ github.actor == 'dependabot[bot]' }}
steps:
- name: Dependabot metadata
id: dependabot-metadata
uses: dependabot/fetch-metadata@v1.1.1
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
- name: Enable auto-merge for Dependabot PRs
if: ${{ steps.dependabot-metadata.outputs.update-type == 'version-update:semver-patch' || steps.dependabot-metadata.outputs.update-type == 'version-update:semver-minor'}}
run: gh pr merge --auto --merge "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
32 changes: 32 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: build

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master

- name: Set up Go
uses: actions/setup-go@master
with:
go-version: ">=1.20.0"

- name: Build
run: go build -v ./...

- name: Test
run: go test -v ./... -race -coverprofile=coverage.txt -covermode=atomic

- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

- name: Refresh Go Report Card
uses: creekorful/goreportcard-action@master
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,44 @@
[![codecov](https://codecov.io/gh/gofika/httputil/branch/main/graph/badge.svg)](https://codecov.io/gh/gofika/httputil)
[![Build Status](https://github.com/gofika/httputil/workflows/build/badge.svg)](https://github.com/gofika/httputil)
[![go.dev](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white)](https://pkg.go.dev/github.com/gofika/httputil)
[![Go Report Card](https://goreportcard.com/badge/github.com/gofika/httputil)](https://goreportcard.com/report/github.com/gofika/httputil)
[![Licenses](https://img.shields.io/github/license/gofika/httputil)](LICENSE)

# httputil

golang http utils for common use


## Basic Usage

### Installation

To get the package, execute:

```bash
go get github.com/gofika/httputil
```

### Example

```go
package main

import (
"fmt"

"github.com/gofika/httputil"
)

func main() {
resp, err := httputil.Get("https://httpbin.org/get")
if err != nil {
panic(err)
}
type GetResp struct {
URL string `json:"url"`
}
res, err := ReadJSON[GetResp](resp)
fmt.Printf("url=%s\n", res.URL)
}
```
169 changes: 169 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package httputil

import (
"bytes"
"context"
"encoding/json"
"io"
"mime/multipart"
"net"
"net/http"
"net/http/cookiejar"
"net/url"
"time"

"golang.org/x/net/publicsuffix"
)

type Client struct {
client *http.Client
ctx context.Context
opts *ClientOptions
}

// NewClient new client
func NewClient(ctx context.Context, opts ...ClientOption) *Client {
jar, _ := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
options := &ClientOptions{
timeout: time.Minute,
dialTimeout: 30 * time.Second,
keepAliveTimeout: 30 * time.Second,
maxIdleConns: 100,
idleConnTimeout: 90 * time.Second,
tlsHandshakeTimeout: 10 * time.Second,
expectContinueTimeout: 1 * time.Second,
}
for _, opt := range opts {
opt(options)
}
var transport http.RoundTripper
if options.proxy != "" {
transport = &http.Transport{
Proxy: func(_ *http.Request) (*url.URL, error) {
return url.Parse(options.proxy)
},
DialContext: (&net.Dialer{
Timeout: options.dialTimeout,
KeepAlive: options.keepAliveTimeout,
}).DialContext,
MaxIdleConns: options.maxIdleConns,
IdleConnTimeout: options.idleConnTimeout,
TLSHandshakeTimeout: options.tlsHandshakeTimeout,
ExpectContinueTimeout: options.expectContinueTimeout,
}
}
return &Client{
client: &http.Client{
Jar: jar,
Transport: transport,
},
ctx: ctx,
opts: options,
}
}

// Close close
func (c *Client) Close() {
c.client.CloseIdleConnections()
}

func (c *Client) fillHeader(header http.Header, opts *RequestOptions) {
if c.opts.userAgent != "" {
header.Set("User-Agent", c.opts.userAgent)
}
if opts.referer != "" {
header.Set("Referer", opts.referer)
}
if opts.contentType != "" {
header.Set("Content-Type", opts.contentType)
}
for key, values := range opts.headers {
for _, value := range values {
if header.Get(key) != "" {
continue
}
header.Add(key, value)
}
}
}

// Do do
func (c *Client) Do(req *http.Request, opts ...RequestOption) (resp *http.Response, err error) {
options := &RequestOptions{}
for _, opt := range opts {
opt(options)
}
c.fillHeader(req.Header, options)
timeout := c.opts.timeout
var timeoutZero time.Duration
if options.timeout != timeoutZero {
timeout = options.timeout
}
ctx, cancel := context.WithTimeout(c.ctx, timeout)
defer cancel()
return c.client.Do(req.WithContext(ctx))
}

// Get get
func (c *Client) Get(url string, opts ...RequestOption) (resp *http.Response, err error) {
req, err := http.NewRequestWithContext(c.ctx, http.MethodGet, url, nil)
if err != nil {
return
}
return c.Do(req, opts...)
}

// Post post
func (c *Client) Post(url string, contentType string, body io.Reader, opts ...RequestOption) (resp *http.Response, err error) {
req, err := http.NewRequestWithContext(c.ctx, http.MethodPost, url, body)
if err != nil {
return
}
return c.Do(req, append([]RequestOption{WithContentType(contentType)}, opts...)...)
}

// PostForm post form
func (c *Client) PostForm(url string, data url.Values, opts ...RequestOption) (resp *http.Response, err error) {
return c.Post(url, "application/x-www-form-urlencoded", bytes.NewBufferString(data.Encode()), opts...)
}

// PostJSON post json
func (c *Client) PostJSON(url string, body any, opts ...RequestOption) (resp *http.Response, err error) {
payload, err := json.Marshal(body)
if err != nil {
return
}
return c.Post(url, "application/json", bytes.NewBuffer(payload), opts...)
}

// PostFormFiles post form files
func (c *Client) PostFormFiles(url string, data url.Values, files []*UploadFile, opts ...RequestOption) (resp *http.Response, err error) {
body := new(bytes.Buffer)
writer := multipart.NewWriter(body)
contentType := writer.FormDataContentType()
for k, i := range data {
for _, j := range i {
err = writer.WriteField(k, j)
if err != nil {
return nil, err
}
}
}
for _, file := range files {
part, err := writer.CreateFormFile(file.FieldName, file.FileName)
if err != nil {
return nil, err
}
if _, err := io.Copy(part, file.Body); err != nil {
return nil, err
}
}
if err := writer.Close(); err != nil {
return nil, err
}
req, err := http.NewRequest(http.MethodPost, url, body)
if err != nil {
return nil, err
}
return c.Do(req, append([]RequestOption{WithContentType(contentType), WithRequestTimeout(time.Hour)}, opts...)...)
}
7 changes: 7 additions & 0 deletions funcs_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package httputil

const HTTPBIN_ENDPOINT = "https://httpbin.org"

func endpoint(path string) string {
return HTTPBIN_ENDPOINT + path
}
17 changes: 17 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module github.com/gofika/httputil

go 1.21.6

require (
github.com/gofika/fileutil v0.0.0-20240131101026-06f7b430e64a
github.com/gofika/regexputil v0.0.0-20240131104326-2f03e34692bf
github.com/stretchr/testify v1.8.4
golang.org/x/net v0.20.0
golang.org/x/text v0.14.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
17 changes: 17 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gofika/fileutil v0.0.0-20240131101026-06f7b430e64a h1:I1wpuzL0HhjVIqc0QuJsomTzzuHuDSRoNvrXDWyDS9Y=
github.com/gofika/fileutil v0.0.0-20240131101026-06f7b430e64a/go.mod h1:1iv1d8ir1VA4s6JPGo2pccK0rEKOi/drjedmEjCvmLo=
github.com/gofika/regexputil v0.0.0-20240131104326-2f03e34692bf h1:d4Ne7Bize9Scv0temnCXDnkVEng5Op6V09iMB42llYw=
github.com/gofika/regexputil v0.0.0-20240131104326-2f03e34692bf/go.mod h1:YjO232yyw1+pMF+oSsx02NWrULX/RTdS4j/6yxb2Odo=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Loading

0 comments on commit ee35e2d

Please sign in to comment.