HTTP Get request to IP-based host using Indy - http

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 );

Related

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.

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

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.

Read form POST variables with nginx

I have a client program I cannot modify. It makes large POST (x-www-form-urlencoded) requests containing hundreds of variables across WAN links, but I only need 5 of them. I'm inserting nginx as a reverse proxy on the local client system. What's the easiest to get nginx to strip out the extra data?
Two ways I see so far:
1. Use Lua (If I did, should I do content_by_lua, rewrite the body, and then make a subrequest? Or is there a simpler way?)
2. Use form-input-nginx-module and proxy_set_body to parse and grab a few variables out.
I'm already using OpenResty, so Lua means no extra modules. But, it probably means writing more locations and so on to do subrequests.
In my opinion the easiest way will be using lua. The choice between content_by_lua, rewrite_by_lua, access_by_lua or any combination of them; will depend on how you use the response body of your subrequest. That decision will also determine if you would need additional locations or not.
Here are a couple of examples:
1. with content_by_lua targeting a local location.
(This approach requires the definition of the sub request location)
location /original/url {
lua_need_request_body on;
content_by_lua '
--Lots of params but I only need 5 for the subrequest
local limited_post_args, err = ngx.req.get_post_args(5)
if not limited_post_args then
ngx.say("failed to get post args: ", err)
return
end
local subreq_uri = "/test/local"
local subreq_response = ngx.location.capture(subreq_uri, {method=ngx.HTTP_POST,
body = ngx.encode_args(limited_post_args)})
ngx.print(subreq_response.body)
';
}
location ~/test/local {
lua_need_request_body on;
proxy_set_header Accept-Encoding "";
proxy_pass http://echo.200please.com;
}
2. with rewrite_by_lua to remote target
(No additional location is needed)
location /original/url/to/remote {
lua_need_request_body on;
rewrite_by_lua '
--Lost of params but I only need 5 for the subrequest
local limited_post_args, err = ngx.req.get_post_args(5)
if not limited_post_args then
ngx.say("failed to get post args: ", err)
return
end
--setting limited number of params
ngx.req.set_body_data(ngx.encode_args(limited_post_args))
--rewriting url
local subreq_path = "/test"
ngx.req.set_uri(subreq_path)
';
proxy_pass http://echo.200please.com;
}
Sample post request with 7 args limited to 5:
curl 'http://localhost/original/url/to/remote' --data 'param1=test&param2=2&param3=3&param4=4&param5=5&param6=6&param7=7' --compressed
response:
POST /test HTTP/1.0
Host: echo.200please.com
Connection: close
Content-Length: 47
User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2
Accept: */*
Accept-Encoding: deflate, gzip
Content-Type: application/x-www-form-urlencoded
param3=3&param4=4&param1=test&param2=2&param5=5

How to add HTTP Header in POST packet?

I'm trying to send a POST request which needs to modify the Header.
Here is my code:
import (
"net/http"
"net/url"
"fmt"
)
const API_URL = "https://api.site.com/api/"
func SendOne(str string) {
v := url.Values{}
v.Add("source", "12345678")
v.Add("text", str)
client := &http.Client{nil, nil, nil}
req, err := http.NewRequest("POST", API_URL, strings.NewReader(v.Encode()))
if err != nil {
fmt.Println(err)
}
req.Header.Add("Authorization", "123456")
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
}
defer res.Body.Close()
}
I have no idea why the code doesn't work. Any clue?
Thanks in advance.
Edit: I forgot to say I was using OAuth 2.0 for authorization.
Using tcpdump we can see that the request headers and body for the code you pasted looks like:
POST / HTTP/1.1
Host: example.com
User-Agent: Go 1.1 package http
Content-Length: 45
Authorization: 123456
Accept-Encoding: gzip
source=12345678&text=http%3A%2F%2Fexample.com
You mention in the comment above that if you add a Content-Type header it works. Doing the same process and dumping the communication between the two peers we get:
POST / HTTP/1.1
Host: example.com
User-Agent: Go 1.1 package http
Content-Length: 45
Authorization: 123456
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip
source=12345678&text=http%3A%2F%2Fexample.com
Which is exactly the same as the prior payload, except it now includes the provided Content-Type header. So, in terms of the behavior within the Go application itself, there's nothing special happening other than what you explicitly told it to do.
The reason why it works when you add the Content-Type header then must be that the actual server you're talking to wants to know how the content body you're providing is encoded.

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