Files
ciimsproxy/internal/http.go
T

119 lines
2.5 KiB
Go
Raw Normal View History

2020-05-11 14:58:22 +08:00
package internal
import (
"context"
2026-07-08 15:17:07 +08:00
"fmt"
"io"
2020-05-11 14:58:22 +08:00
"net/http"
"strings"
"time"
)
const (
defaultContentType string = "text/xml; charset=UTF-8"
defaultAgent string = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; XFire Client +http://xfire.codehaus.org)"
maxResponseBytes = 64 << 20 // 64 MiB
2020-05-11 14:58:22 +08:00
)
2026-07-08 15:17:07 +08:00
type Client struct {
httpClient *http.Client
2020-05-11 14:58:22 +08:00
}
2026-07-08 15:17:07 +08:00
type CIIMSResponse struct {
RawXML string
}
2020-05-11 14:58:22 +08:00
2026-07-08 15:17:07 +08:00
type HTTPStatusError struct {
StatusCode int
Status string
Body string
}
type ResponseTooLargeError struct {
Limit int64
}
2026-07-08 15:17:07 +08:00
func (e *HTTPStatusError) Error() string {
body := e.Body
if len(body) > 512 {
body = body[:512] + "..."
}
if body == "" {
return fmt.Sprintf("ciims returned %s", e.Status)
}
return fmt.Sprintf("ciims returned %s: %s", e.Status, body)
}
func (e *ResponseTooLargeError) Error() string {
return fmt.Sprintf("ciims response exceeds %d bytes", e.Limit)
}
2026-07-08 15:17:07 +08:00
func NewClient(timeoutSec int) (*Client, error) {
if timeoutSec <= 0 {
return nil, fmt.Errorf("timeout must be positive")
}
return &Client{
httpClient: &http.Client{
Timeout: time.Duration(timeoutSec) * time.Second,
},
}, nil
}
func (r *CIIMSResponse) IsFault() bool {
return r != nil && r.ErrorMessage() != ""
}
func (r *CIIMSResponse) ErrorMessage() string {
if r == nil {
return ""
}
return GetErrMsg(r.RawXML)
}
func (r *CIIMSResponse) Messages() []string {
if r == nil {
return nil
}
return GetMsgs(r.RawXML)
}
func (c *Client) Send(ctx context.Context, url string, msg string) (*CIIMSResponse, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, strings.NewReader(msg))
2020-05-11 14:58:22 +08:00
if err != nil {
2026-07-08 15:17:07 +08:00
return nil, err
2020-05-11 14:58:22 +08:00
}
req.Header.Set("Content-Type", defaultContentType)
req.Header.Set("User-Agent", defaultAgent)
req.Header.Set("SOAPAction", "")
2026-07-08 15:17:07 +08:00
resp, err := c.httpClient.Do(req)
2020-05-11 14:58:22 +08:00
if err != nil {
2026-07-08 15:17:07 +08:00
return nil, err
2020-05-11 14:58:22 +08:00
}
defer resp.Body.Close()
body, err := readLimited(resp.Body, maxResponseBytes)
2020-05-11 14:58:22 +08:00
if err != nil {
2026-07-08 15:17:07 +08:00
return nil, err
2020-05-11 14:58:22 +08:00
}
2026-07-08 15:17:07 +08:00
ciimsResp := &CIIMSResponse{RawXML: body}
if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {
return ciimsResp, &HTTPStatusError{
StatusCode: resp.StatusCode,
Status: resp.Status,
Body: body,
}
}
return ciimsResp, nil
2020-05-11 14:58:22 +08:00
}
func readLimited(r io.Reader, limit int64) (string, error) {
limited := io.LimitReader(r, limit+1)
respBytes, err := io.ReadAll(limited)
if err != nil {
return "", err
}
if int64(len(respBytes)) > limit {
return "", &ResponseTooLargeError{Limit: limit}
}
return string(respBytes), nil
}