diff --git a/client.go b/client.go index de1d54d..aee6246 100644 --- a/client.go +++ b/client.go @@ -1,6 +1,7 @@ package osrm import ( + "bytes" "context" "encoding/json" "fmt" @@ -21,28 +22,29 @@ type ( client struct { httpClient HTTPClient serverURL string + usePOST bool } ) // newClient creates a client with server url and specific getter -func newClient(serverURL string, c HTTPClient) client { - return client{c, serverURL} +func newClient(httpClient HTTPClient, serverURL string, usePOST bool) client { + return client{httpClient: httpClient, serverURL: serverURL, usePOST: usePOST} } // doRequest makes GET request to OSRM server and decodes the given JSON func (c client) doRequest(ctx context.Context, in *request, out interface{}) error { - url, err := in.URL(c.serverURL) + path, err := in.URLPath() if err != nil { return err } - resp, err := c.get(ctx, url) + resp, err := c.httpRequest(ctx, path) if err != nil { return err } defer closeSilently(resp.Body) - bytes, err := ioutil.ReadAll(resp.Body) + body, err := ioutil.ReadAll(resp.Body) if err != nil { return fmt.Errorf("failed to read body: %v", err) } @@ -51,17 +53,25 @@ func (c client) doRequest(ctx context.Context, in *request, out interface{}) err // In other cases, it returns an unexpected error without a body. // http://project-osrm.org/docs/v5.5.1/api/#responses if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusBadRequest { - return fmt.Errorf("unexpected http status code %d with body %q", resp.StatusCode, bytes) + return fmt.Errorf("unexpected http status code %d with body %q", resp.StatusCode, body) } - if err := json.Unmarshal(bytes, out); err != nil { - return fmt.Errorf("failed to unmarshal body %q: %v", bytes, err) + if err := json.Unmarshal(body, out); err != nil { + return fmt.Errorf("failed to unmarshal body %q: %v", body, err) } return nil } -func (c client) get(ctx context.Context, url string) (*http.Response, error) { +func (c client) httpRequest(ctx context.Context, path string) (*http.Response, error) { + if c.usePOST { + return c.post(ctx, path) + } + return c.get(ctx, path) +} + +func (c client) get(ctx context.Context, path string) (*http.Response, error) { + url := c.serverURL + "/" + path req, err := http.NewRequest("GET", url, nil) if err != nil { return nil, err @@ -70,6 +80,16 @@ func (c client) get(ctx context.Context, url string) (*http.Response, error) { return c.httpClient.Do(req.WithContext(ctx)) } +func (c client) post(ctx context.Context, path string) (*http.Response, error) { + req, err := http.NewRequest("POST", c.serverURL, bytes.NewReader([]byte(path))) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", "application/x-uri") + return c.httpClient.Do(req.WithContext(ctx)) +} + func closeSilently(c io.Closer) { // #nosec - make github.com/GoASTScanner/gas linter ignore this _ = c.Close() // nothing meaningful to do with this error - so ignore and suppress linter warnings diff --git a/osrm.go b/osrm.go index 8a7ebeb..21c6e31 100644 --- a/osrm.go +++ b/osrm.go @@ -28,6 +28,8 @@ type Config struct { // Client is custom pre-configured http client to be used for queries. // New http.Client instance with default settings and one second timeout will be used if not set. Client HTTPClient + // Use POST method to request OSRM API + UsePOST bool } // ResponseStatus represent OSRM API response @@ -84,7 +86,7 @@ func NewWithConfig(cfg Config) *OSRM { cfg.Client = &http.Client{Timeout: defaultTimeout} } - return &OSRM{client: newClient(cfg.ServerURL, cfg.Client)} + return &OSRM{client: newClient(cfg.Client, cfg.ServerURL, cfg.UsePOST)} } func (o OSRM) query(ctx context.Context, in *request, out response) error { diff --git a/types.go b/types.go index 8f5a559..44c6fea 100644 --- a/types.go +++ b/types.go @@ -182,8 +182,8 @@ type request struct { options options } -// URL generates a url for OSRM request -func (r *request) URL(serverURL string) (string, error) { +// URLPath generates a url path for OSRM request +func (r *request) URLPath() (string, error) { if r.service == "" { return "", ErrEmptyServiceName } @@ -193,18 +193,17 @@ func (r *request) URL(serverURL string) (string, error) { if r.coords.Length() == 0 { return "", ErrNoCoordinates } - // http://{server}/{service}/{version}/{profile}/{coordinates}[.{format}]?option=value&option=value - url := strings.Join([]string{ - serverURL, // server + // {service}/{version}/{profile}/{coordinates}[.{format}]?option=value&option=value + u := strings.Join([]string{ r.service, // service version, // version r.profile, // profile "polyline(" + url.PathEscape(r.coords.Polyline(polyline5Factor)) + ")", // coordinates }, "/") if len(r.options) > 0 { - url += "?" + r.options.encode() // options + u += "?" + r.options.encode() // options } - return url, nil + return u, nil } // Bearing limits the search to segments with given bearing in degrees towards true north in clockwise direction.