Allow passing embedded "ReturnHeaders" object to get response headers in handlers if necessary e.g. captcha cookie?

Parse set-cookie header and store session cookie in client if set-cookie contains connect.sid cookie
Remove some json definitions for option arguments to client methods
master
Thomas Lynch 2 years ago
parent 0bd911d6bf
commit 3fac0d046f
  1. 40
      app/auth.go
  2. 20
      app/boardlist.go
  3. 42
      app/jschan.go
  4. 14
      app/overboard.go
  5. 21
      example.go

@ -0,0 +1,40 @@
package jschan
import (
"context"
"fmt"
"net/http"
"net/url"
"strings"
)
type PostLoginOptions struct {
Username string
Password string
Twofactor string
}
func (c *Client) Login(ctx context.Context, options *PostLoginOptions) error {
formData := url.Values{}
formData.Set("username", options.Username)
formData.Set("password", options.Password)
formData.Set("twofactor", options.Twofactor)
endodedBody := strings.NewReader(formData.Encode())
url := fmt.Sprintf("%s/forms/login", c.BaseURL)
req, err := http.NewRequest(http.MethodPost, url, endodedBody)
if err != nil {
return err
}
req = req.WithContext(ctx)
if err := c.sendRequest(req, nil, nil); err != nil {
return err
}
return nil
}

@ -15,12 +15,12 @@ type GetBoardsResponse struct {
} }
type GetBoardsPublicOptions struct { type GetBoardsPublicOptions struct {
Search string `json:"search"` Search string
Sort string `json:"sort"` Sort string
SortDirection string `json:"direction"` SortDirection string
Page int `json:"page"` Page int
LocalFirst bool `json:"local_first"` LocalFirst bool
Sites []string `json:"sites"` Sites []string
} }
func (c *Client) GetBoardsPublic(ctx context.Context, options *GetBoardsPublicOptions) (*GetBoardsResponse, error) { func (c *Client) GetBoardsPublic(ctx context.Context, options *GetBoardsPublicOptions) (*GetBoardsResponse, error) {
@ -54,7 +54,7 @@ func (c *Client) GetBoardsPublic(ctx context.Context, options *GetBoardsPublicOp
url := fmt.Sprintf("%s/boards.json?%s", c.BaseURL, query.Encode()) url := fmt.Sprintf("%s/boards.json?%s", c.BaseURL, query.Encode())
req, err := http.NewRequest("GET", url, nil) req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -62,7 +62,7 @@ func (c *Client) GetBoardsPublic(ctx context.Context, options *GetBoardsPublicOp
req = req.WithContext(ctx) req = req.WithContext(ctx)
res := GetBoardsResponse{} res := GetBoardsResponse{}
if err := c.sendRequest(req, &res); err != nil { if err := c.sendRequest(req, &res, nil); err != nil {
return nil, err return nil, err
} }
@ -116,7 +116,7 @@ func (c *Client) GetBoardsGlobalmanage(ctx context.Context, options *GetBoardsGl
url := fmt.Sprintf("%s/boards.json?%s", c.BaseURL, query.Encode()) url := fmt.Sprintf("%s/boards.json?%s", c.BaseURL, query.Encode())
req, err := http.NewRequest("GET", url, nil) req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil { if err != nil {
return nil, err return nil, err
@ -125,7 +125,7 @@ func (c *Client) GetBoardsGlobalmanage(ctx context.Context, options *GetBoardsGl
req = req.WithContext(ctx) req = req.WithContext(ctx)
res := GetBoardsResponse{} res := GetBoardsResponse{}
if err := c.sendRequest(req, &res); err != nil { if err := c.sendRequest(req, &res, nil); err != nil {
return nil, err return nil, err
} }

@ -22,6 +22,9 @@ func NewClient(baseURL string) *Client {
CsrfToken: "", CsrfToken: "",
HTTPClient: &http.Client{ HTTPClient: &http.Client{
Timeout: time.Minute, Timeout: time.Minute,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}, },
} }
} }
@ -32,9 +35,27 @@ type DynamicResponse struct {
Redirect string `json:"redirect,omitempty"` Redirect string `json:"redirect,omitempty"`
} }
func (c *Client) sendRequest(req *http.Request, v interface{}) error { type ReturnHeaders struct {
*http.Header
}
func cookieHeader(rawCookies string) []*http.Cookie {
header := http.Header{}
header.Add("Cookie", rawCookies)
req := http.Request{Header: header}
return req.Cookies()
}
func (c *Client) sendRequest(req *http.Request, v interface{}, h *ReturnHeaders) error {
if req.Method == http.MethodGet {
req.Header.Set("Content-Type", "application/json; charset=utf-8")
} else {
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
}
req.Header.Set("Accept", "application/json; charset=utf-8") req.Header.Set("Accept", "application/json; charset=utf-8")
//req.Header.Set("Content-Type", "application/json; charset=utf-8") req.Header.Set("X-Using-XHR", "true")
req.Header.Set("Referer", c.BaseURL)
if c.SessionCookie != "" { if c.SessionCookie != "" {
req.Header.Set("Cookie", fmt.Sprintf("connect.sid=%s", c.SessionCookie)) req.Header.Set("Cookie", fmt.Sprintf("connect.sid=%s", c.SessionCookie))
} }
@ -56,9 +77,20 @@ func (c *Client) sendRequest(req *http.Request, v interface{}) error {
return fmt.Errorf("unknown error, status code: %d", res.StatusCode) return fmt.Errorf("unknown error, status code: %d", res.StatusCode)
} }
fullResponse := v h = &ReturnHeaders{
if err = json.NewDecoder(res.Body).Decode(&fullResponse); err != nil { &res.Header,
return err }
setCookieValue := h.Get("Set-Cookie")
if setCookieValue != "" {
parsedSetCookie := cookieHeader(setCookieValue)
c.SessionCookie = parsedSetCookie[0].Value
}
if v != nil {
fullResponse := v
if err = json.NewDecoder(res.Body).Decode(&fullResponse); err != nil {
return err
}
} }
return nil return nil

@ -10,9 +10,9 @@ import (
) )
type GetOverboardOptions struct { type GetOverboardOptions struct {
AddBoards []string `json:"search"` AddBoards []string
RemoveBoards []string `json:"sort"` RemoveBoards []string
IncludeDefault bool `json:"include_default"` IncludeDefault bool
} }
type GetOverboardResponse struct { type GetOverboardResponse struct {
@ -45,7 +45,7 @@ func (c *Client) GetOverboardIndex(ctx context.Context, options *GetOverboardOpt
url := fmt.Sprintf("%s/overboard.json?%s", c.BaseURL, query.Encode()) url := fmt.Sprintf("%s/overboard.json?%s", c.BaseURL, query.Encode())
req, err := http.NewRequest("GET", url, nil) req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -53,7 +53,7 @@ func (c *Client) GetOverboardIndex(ctx context.Context, options *GetOverboardOpt
req = req.WithContext(ctx) req = req.WithContext(ctx)
res := GetOverboardResponse{} res := GetOverboardResponse{}
if err := c.sendRequest(req, &res); err != nil { if err := c.sendRequest(req, &res, nil); err != nil {
return nil, err return nil, err
} }
@ -70,7 +70,7 @@ func (c *Client) GetOverboardCatalog(ctx context.Context, options *GetOverboardO
url := fmt.Sprintf("%s/catalog.json?%s", c.BaseURL, query.Encode()) url := fmt.Sprintf("%s/catalog.json?%s", c.BaseURL, query.Encode())
req, err := http.NewRequest("GET", url, nil) req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -78,7 +78,7 @@ func (c *Client) GetOverboardCatalog(ctx context.Context, options *GetOverboardO
req = req.WithContext(ctx) req = req.WithContext(ctx)
res := GetOverboardResponse{} res := GetOverboardResponse{}
if err := c.sendRequest(req, &res); err != nil { if err := c.sendRequest(req, &res, nil); err != nil {
return nil, err return nil, err
} }

@ -8,15 +8,26 @@ import (
func main() { func main() {
jschanClient := jschan.NewClient("https://ptchan.org") jschanClient := jschan.NewClient("https://fatchan.org")
ctx := context.Background() ctx := context.Background()
options := &jschan.GetOverboardOptions{
IncludeDefault: true, loginOptions := &jschan.PostLoginOptions{
Username: "",
Password: "",
Twofactor: "",
}
err := jschanClient.Login(ctx, loginOptions)
if err != nil {
fmt.Println(err)
return
} }
res, err := jschanClient.GetOverboardCatalog(ctx, options) overboardOptions := &jschan.GetOverboardOptions{
IncludeDefault: true,
}
res, err := jschanClient.GetOverboardCatalog(ctx, overboardOptions)
if err != nil { if err != nil {
fmt.Println("an error occurred") fmt.Println(err)
return return
} }

Loading…
Cancel
Save