How to add a "Authorization=Bearer" header with Indy in Delphi? - http

I'm trying to do a POST request using an access_token, and it works fine using POSTMAN, but when I try to do the same request on Delphi, I can't find a way to add the "Authorization=Bearer eyxxxxxx..." to the Request header, as POSTMAN does.
POSTMAN Request (working well):
POST /somepath HTTP/1.1
Host: someurl.com.br
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.....
Content-Type: application/json
(body content ommited)
Indy Request generated by Delphi, captured by HTTP Analyzer (always returning 401 Forbidden error, because the absence of "Authorization=Bearer" part):
POST /somepath HTTP/1.1
Host: someurl.com.br
Content-Type: application/json
(body content ommited)
I've tried to add the header using the code below, but the header part with the "Authorization=Bearer eyxxxxxx..." isn't generated on Request, returning the 401 Forbidden error.
FIdHTTP.Request.CustomHeaders.FoldLines := False;
FIdHTTP.Request.CustomHeaders.Add('Authorization=Bearer ' + txtToken.Text);

Just found the problem. I added the wrong separator between the "Authorization" and "Bearer" words.
Wrong:
FIdHTTP.Request.CustomHeaders.FoldLines := False;
FIdHTTP.Request.CustomHeaders.Add('Authorization=Bearer ' + txtToken.Text);
Correct:
FIdHTTP.Request.CustomHeaders.FoldLines := False;
FIdHTTP.Request.CustomHeaders.Add('Authorization:Bearer ' + txtToken.Text);
After replacing the '=' by ':', I received the expected response, like the one received by POSTMAN.

Related

PactNet - HttpPost Test Fails with 500 internal server error

I am trying to send an HTTP Post request from my test method to my Pactnet mock service. The following is the log generated -
[INFO][pact_mock_server::hyper_server] Received request HTTP Request ( method: POST, path: /api/v1/post-txn, query: None, headers: Some({"host": ["127.0.0.1:62047"], "content-length": ["160"], "content-type": ["application/json; charset=utf-8"]}), body: Present(160 bytes, application/json;charset=utf-8) )
[INFO][pact_matching] comparing to expected HTTP Request ( method: POST, path: /api/v1/post-txn, query: None, headers: Some({"Content-Type": ["application/json; charset=utf-8"]}), body: Present(114 bytes, application/json) )
For me, it looks like the received request and the expected request look the same from the log information. However, the test is unsuccessful with the below exception message -
{StatusCode: 500, ReasonPhrase: 'Internal Server Error', Version: 1.1, Content: System.Net.Http.HttpConnectionResponseContent, Headers:
{
Access-Control-Allow-Origin: *
x-pact: Request-Mismatch
Date: Thu, 24 Mar 2022 05:16:31 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 648
}}
Could someone help me what is wrong with my received request and expected request, and where there is a mismatch as mentioned in the exception details? I have spent a lot of time debugging, yet I am unable to find what exactly the issue is. Thanks in advance.
If you increase the log level to debug you should be able to compare the JSON body in the request to what's expected. The body looks to be different as I dictated by the number of bytes expected vs actual.
Additionally, there should be an error message to indicate why it failed. If not that could be a bug.
The header content type also looks different on close inspection

How to intercept bad http HEAD request

Is there a way to intercept a bad HEAD request in a Go HTTP server? A bad request here would be to send a JSON payload with a HEAD request. I call this a Bad Request, but when I attempt a HEAD request with a body via curl, I get this error. However, no logging occurs in Go.
package main
import (
"fmt"
"log"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
log.Println(r.Method, r.URL)
_, _ = fmt.Fprintf(w, "Hello")
}
func main() {
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
If I send a curl request without a body, it works as expected and a log entry is generated 2019/11/28 10:58:59 HEAD / .
$ curl -v -X HEAD http://localhost:8080
curl -i -X HEAD http://localhost:8080
Warning: Setting custom HTTP method to HEAD with -X/--request may not work the
Warning: way you want. Consider using -I/--head instead.
HTTP/1.1 200 OK
Date: Thu, 28 Nov 2019 16:03:22 GMT
Content-Length: 5
Content-Type: text/plain; charset=utf-8
However, if I send a curl request with a body, then I get a Bad Request status but no log is updated.
$ curl -i -X HEAD http://localhost:8080 -d '{}'
Warning: Setting custom HTTP method to HEAD with -X/--request may not work the
Warning: way you want. Consider using -I/--head instead.
HTTP/1.1 400 Bad Request
Content-Type: text/plain; charset=utf-8
Connection: close
400 Bad Request
I want to catch this error so I can send my own custom error message back. How can I intercept this?
You can't. The HTTP server of the standard lib does not provide any interception point or callback for this case.
The invalid request is "killed" before your handler would be called. You can see this in server.go, conn.serve() method:
w, err := c.readRequest(ctx)
// ...
if err != nil {
switch {
// ...
default:
publicErr := "400 Bad Request"
if v, ok := err.(badRequestError); ok {
publicErr = publicErr + ": " + string(v)
}
fmt.Fprintf(c.rwc, "HTTP/1.1 "+publicErr+errorHeaders+publicErr)
return
}
}
// ...
serverHandler{c.server}.ServeHTTP(w, w.req)
Go's HTTP server provides you an implementation to handle incoming requests from clients that use / adhere to the HTTP protocol. All browsers and notable clients follow the HTTP protocol. It's not the implementation's goal to provide a fully customizable server.

HTTP Get request to IP-based host using Indy

I have some Delphi code that connects to a servlet and I´m trying to switch from TIdTCPClient to TIdHTTP.
I connect to the servlet this way
try
lHTTP := TIdHTTP.Create( nil );
responseStream := TMemoryStream.Create;
lHTTP.Get(HttpMsg, responseStream);
SetString( html, PAnsiChar(responseStream.Memory), responseStream.Size);
AnotarMensaje( odDepurar, 'IMPFIS: Impresora fiscal reservada ' + html );
Where HttpMsg is localhost:6080/QRSRPServer/PedirImpresion?usuarioDMS=hector
All I´m getting is
GET localhost:6080/QRSRPServer/PedirImpresion?usuarioDMS=hector HTTP/1.1
Content-Type: text/html
Accept: text/html, */*
User-Agent: Mozilla/3.0 (compatible; Indy Library)
HTTP/1.1 400 Bad Request
The HTTP dialog that I had before was like this
GET /QRSRPServer/PedirImpresion?usuarioDMS=hector HTTP/1.1
Host: localhost:6080
HTTP/1.1 200 OK
So, I try to add the Host header, with this host: localhost:6080
try
lHTTP := TIdHTTP.Create( nil );
lHTTP.Host := Host;
responseStream := TMemoryStream.Create;
lHTTP.Get(HttpMsg, responseStream);
SetString( html, PAnsiChar(responseStream.Memory), responseStream.Size);
AnotarMensaje( odDepurar, 'IMPFIS: Impresora fiscal reservada ' + html );
And I get
Socket Error # 11004
Where HttpMsg is localhost:6080/QRSRPServer/PedirImpresion?usuarioDMS=hector
HttpMsg must begin with http:// or https://:
http://localhost:6080/QRSRPServer/PedirImpresion?usuarioDMS=hector
You should be getting an EIdUnknownProtocol exception raised when TIdHTTP parses the URL and sees the missing protocol scheme.
TIdHTTP should always be sending a Host header, but especially for an HTTP 1.1 request, but you claim it is not. This is why you are getting a Bad Request error, because HTTP 1.1 servers are required to reject an HTTP 1.1 request that omits that header.
You also claim that TIdHTTP is including the host and port values in the GET line. The ONLY time it ever does that is when connecting to a host through an HTTP proxy, but I don't see you configuring the TIdHTTP.ProxyParams property at all.
In short, TIdHTTP should not be behaving the way you claim.
The correct solution is to make sure you are passing a full URL to TIdHTTP.Get().
On a side note, your code requires html to be an AnsiString. You should change it to a standard string (which is AnsiString in D2007 and earlier) and let TIdHTTP return a string for you, then you don't need the TMemoryStream anymore:
html := lHTTP.Get(HttpMsg);
It was easier than I thought. I was assuming that having a "host" paremeter that included the port would be enough but looking at a Wireshark capture I saw it was sending everything over the standard HTTP port.
So this did the trick
try
lHTTP := TIdHTTP.Create( nil );
lHTTP.Host := GatewayIp;
lHTTP.Port := GatewayPuerto;
responseStream := TMemoryStream.Create;
lHTTP.Request.CustomHeaders.Clear;
lHTTP.Request.CustomHeaders.Add('Host: ' + Host );
lHTTP.Get(HttpMsg, responseStream);
SetString( html, PAnsiChar(responseStream.Memory), responseStream.Size);
AnotarMensaje( odDepurar, 'IMPFIS: Impresora fiscal reservada ' + html );

Bad request returned when using the DELETE curl method with Guzzle and sendgrid

I'm trying to use the sendgrid v3 API to purge bounces, it works fine when using CURL in CLI, here is the command:
curl -v -X DELETE -d '{"delete_all": true}' -H "Content-Type: application/json" -H "Authorization: Bearer SG.mykey" "https://api.sendgrid.com/v3/suppression/bounces"
But when trying to launch it with Symfony2 / Guzzle, I am getting a bad request error, however the request seems OK, here is the output of (string) $request:
"""
DELETE /v3/suppression/bounces HTTP/1.1\r\n
Host: api.sendgrid.com\r\n
Authorization: Bearer SG.mykey\r\n
User-Agent: Guzzle/3.9.3 curl/7.35.0 PHP/5.5.9-1ubuntu4.17\r\n
Accept: application/json\r\n
Content-Length: 20\r\n
\r\n
{"delete_all": true}
"""
And the exception:
[Guzzle\Http\Exception\ClientErrorResponseException]
Client error response
[status code] 400
[reason phrase] BAD REQUEST
[url] https://api.sendgrid.com/v3/suppression/bounces
When using the GET method, it works correctly and it returns me all the bounces.
Here is the guzzle code:
$request = $this->httpClient->delete('/v3/suppression/bounces', null, '{"delete_all": true}');
$response = $request->send();
The http client is a service initialized with the https://api.sendgrid.com base URL.
Your problem I think is that you are sending 3 params to delete when it only has two, instead what you need to do is pass the body in options array.
$response = $this->httpClient->delete(
'/v3/suppression/bounces',
[
'body' => json_encode(['delete_all', true]),
'Authorization' => 'Basic ' . base64_encode($username . ':' . $password),
'content-type' => 'application/json'
]
);
Guzzle options docs
Answering to myself. The problem was pretty obvious: The content-type header was not set, the "accept" one was. I didn't care about this header because you don't have to pass it when using the GET method for this API. So now when debugging my request object I have:
"""
DELETE /v3/suppression/bounces HTTP/1.1\r\n
Host: api.sendgrid.com\r\n
Authorization: Bearer SG.mykey\r\n
Content-Type: application/json\r\n
Content-Length: 20\r\n
User-Agent: Guzzle/3.9.3 curl/7.35.0 PHP/5.5.9-1ubuntu4.17\r\n
Accept: application/json\r\n
\r\n
{"delete_all": true}
"""

How to get JSON back from HTTP POST Request (to another domain)

I'm trying to use the API on a website, here's the part of the manual:
Authenticated Sessions (taken from here)
To create an authenticated session, you need to request an authToken from the '/auth' API resource.
URL: http://stage.amee.com/auth (this is not my domain)
Method: POST
Request format: application/x-www-form-urlencoded
Response format: application/xml, application/json
Response code: 200 OK
Response body: Details of the authenticated user, including API
version.
Extra data: "authToken" cookie and header, containing the
authentication token that should be
used for subsequent calls.
Parameters: username / password
Example
Request
POST /auth HTTP/1.1
Accept: application/xml
Content-Type: application/x-www-form-urlencoded
username=my_username&password=my_password
Response
HTTP/1.1 200 OK
Set-Cookie: authToken=1KVARbypAjxLGViZ0Cg+UskZEHmqVkhx/Pm...;
authToken: 1KVARbypAjxLGViZ0Cg+UskZEHmqVkhx/PmEvzkPGp...==
Content-Type: application/xml; charset=UTF-8
QUESTION:
How do I get that to work?
I tried jQuery, but it seems to have problem with XSS. Actual code snippet would be greatly appreciated.
p.s.
All I was looking for was WebClient class in C#
You need to put application/json in your Accept header, this tells the server you want it to respond in that format - not xml.
I am using rails to extract the same authentication token cookie from stage.amee.com/auth as mentioned above. it took a bit of experimentation before I created and customised the correct request object that returned a 200 OK, with the authtoken as a cookie. i haven't found an effective method of reading the request object or I would post exactly what it looks like. here is my ruby code from the app's controller
#define parameters
uri=URI.parse('http://stage.amee.com')
#path = '/auth'
#login_details = 'username=your_username&password=your_password'
#headers = {'Content-Type' => 'application/x-www-form-urlencoded', 'Accept' => 'application/json'}
#create request object
req = Net::HTTP.new(uri.host, uri.port)
#send the request using post, defining the path, body and headers
resp, data = req.post(#path, #login_details, #headers)
#print response details to console
puts "response code = " << resp.code
puts "response inspect = " << resp.inspect
resp.each do |key, val|
puts "response header key : " + key + " = " + val
end
puts "data: " + data

Resources