Selectively Follow Redirects in Go - go

I'm trying to write a twitter reader that resolves the final URLs of link shorteners etc, but gives me a URL along the way for a list of manually defined host patterns. The reason to do this is that i don't want to end up with the paywall URL but the one before.
As far as i can tell the way to do this is write my own client based on the default RoundTripper because returning an error from a custom CheckRedirect function aborts the client without yielding a response.
Is there a way to use the default client and record a list of URLs/specific URL from a custom checkRedirect function?

The client request will actually still return the last valid Response in cases where your custom CheckResponse yields an error (As mentioned in the comments).
http://golang.org/pkg/net/http/#Client
If CheckRedirect returns an error, the Client's Get method returns both the previous Response and CheckRedirect's error (wrapped in a url.Error) instead of issuing the Request req.
If you maintain a list of "known" paywall-urls, you can abort the paywall-redirect in your CheckResponse with a custom error type (Paywalled in the example below).
Your error handling code later has to consider that error type as a special (non-erroneous) case.
Example:
package main
import (
"errors"
"fmt"
"net/http"
"net/url"
)
var Paywalled = errors.New("next redirect would hit a paywall")
var badHosts = map[string]error{
"registration.ft.com": Paywalled,
}
var client = &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
// N.B.: when used in production, also check for redirect loops
return badHosts[req.URL.Host]
},
}
func main() {
resp, err := client.Get("http://on.ft.com/14pQBYE")
// ignore non-nil err if it's a `Paywalled` wrapped in url.Error
if e, ok := err.(*url.Error); (ok && e.Err != Paywalled) || (!ok && err != nil) {
fmt.Println("error: ", err)
return
}
resp.Body.Close()
fmt.Println(resp.Request.URL)
}

Related

How to extract path from user request in golang grpc-gateway

i have a question. Is it possible to extract via metadata path from user request.
Here i have my proto file with defined method.
rpc AllPath(google.protobuf.Empty) returns (google.protobuf.Empty) {
option (google.api.http) = {
get: "/*",
};
}
rpc Auth(google.protobuf.Empty) returns (TokenRender) {
option (google.api.http) = {
get: "/auth"
};
}
}
In AllPath function in my server file im using something like this, found on grpc-gateway ecosystem website.
path := make(map[string]string)
if pattern, ok := runtime.HTTPPathPattern(ctx); ok {
path["pattern"] = pattern // /v1/example/login
}
fmt.Printf("Current path is: %v", path["pattern"])
but my current pattern/path is like i defined in proto file: Current path is: /*
If anyone have idea how to deal with this thing i would appreciate it :)
Best, Kacper
gRPC-Gateway passes various bits of information from the originating HTTP request via gRPC metadata. I don't believe the raw path is supplied, however. It is still possible to get the path passed through by registering a metadata annotator.
When calling github.com/grpc-ecosystem/grpc-gateway/v2/runtime.NewServeMux(), leverage the WithMetadata option func:
mux := runtime.NewServeMux(runtime.WithMetadata(func(_ context.Context, req *http.Request) metadata.MD {
return metadata.New(map[string]string{
"grpcgateway-http-path": req.URL.Path,
})
}))
Then in your gRPC service implementation, you can retrieve the value via the incoming context:
func (s *server) AllPath(ctx context.Context, _ *emptypb.Empty) (*emptypb.Empty, error) {
md, _ := metadata.FromIncomingContext(ctx)
log.Printf("path: %s", md["grpcgateway-http-path"][0])
return &emptypb.Empty{}, nil
}
When hitting, e.g. /foo, this should log:
2022/10/25 15:31:42 path: /foo

Why Google Logging client libraries not logging inside Google cloud functions?

I'm trying to implement a google cloud function to test Google Logging client library. below is my code
// Package p contains an HTTP Cloud Function.
package loggingclient
import (
"cloud.google.com/go/logging"
"net/http"
"context"
"fmt"
)
// HelloWorld prints the JSON encoded "message" field in the body
// of the request or "Hello, World!" if there isn't one.
func HelloWorld(w http.ResponseWriter, r *http.Request) {
label := map[string]string{"priority": "High"}
var projectName = "my-project-id"
ctx := context.Background()
client, err := logging.NewClient(ctx, projectName)
if err != nil {
fmt.Printf("client not created: %v", err)
}
lg := client.Logger("MY-LOGGER")
lg.Log(logging.Entry{
Payload: "Hello, This is error!!",
Severity: logging.Error,
Labels: label,
})
client.Close()
}
Here, I'm expecting a log entry with a message:"Hello, This is error!!" and with a lable:"priority": "High" and severirty "ERROR"
But actually, when I trigger this Cloud Function, I didn't get any new log entries. Therefore don't client logging libraries work inside cloud functions?, How to resolve this?
Thanks
It works on cloud functions. I have done the exact same thing in a cloud function before. You can use google's official documenation with cloud function logging here
Also ensure that the service account have necessary permissions for logging
https://cloud.google.com/logging/docs/access-control

Echo Groups not working with OpenAPI generated code using oapi-codegen

I am using oapi-codegen to generate my server code and Echo Labstack as the server.
When I pass a Group instance to Openapi.RegisterHandlers instead of an Echo instance, I always get a 400 error with {"message":"no matching operation was found"} for any request in that group:
swagger, err := Openapi.GetSwagger()
if err != nil {
fmt.Fprintf(os.Stderr, "Error loading swagger spec\n: %s", err)
os.Exit(1)
}
// Use oapi validation middleware to check all requests against the
// OpenAPI schema.
g := e.Group("/api", middleware.OapiRequestValidator(swagger))
Openapi.RegisterHandlers(g, &MyApi{})
If send request /api/foo, where foo is an API endpoint defined in the generated server code, I get a 400 error. If I do /api/<some undefined api> I also get 400. If I do send a request for /baz, I get 404 as expected, since that isn't a defined route. If I don't pass a prefix to Group(), I get a 400 error for every request. I get the same behavior if I use RegisterHandlersWithBaseURL()
There seems to be a bug where if you specify the a base path, either to the Group() function or to RegisterHandlersWithBaseURL(), theOapiRequestValidator middle ignores the base path when checking the request path against the routes. It uses the routes defined in the OpenAPI spec without the base path. To work around this, I overwrote the inline.tmpl template and hacked the GetSwagger() function to include this at the bottom:
func GetSwagger(pathPrefix string) (swagger *openapi3.T, err error) {
...
var updatedPaths openapi3.Paths = make(openapi3.Paths)
for key, value := range(swagger.Paths) {
updatedPaths[pathPrefix + key] = value
}
swagger.Paths = updatedPaths
}
The key in the Path map is the route. I just append the base path to every key.

Fasthttp error when reading request headers: invalid header key " http/1.1\r\nuser-Agent"

I am just started learning Go, and this question made me stuck.
Trying to test request handling on localhost in testing func using github.com/valyala/fasthttp.
First running the server like in https://github.com/valyala/fasthttp/blob/master/server_example_test.go:
ln, err := net.Listen("tcp", ":8080")
if err != nil {
log.Fatalf("error in net.Listen: %s", err)
}
requestHandler := func(ctx *fasthttp.RequestCtx) {
fmt.Println(ctx, "Requested path is")
}
if err := fasthttp.Serve(ln, requestHandler); err != nil {
log.Fatalf("error in Serve: %s", err)
}
then if I run the request func (FastRequest(url string)) from the same testing function it works fine...
Fasthttp request func:
func FastRequest(url string) error {
Request := &fasthttp.Request{}
Response := &fasthttp.Response{}
FastHTTPClient := &fasthttp.Client{}
Request.SetRequestURI(url)
for {
err := FastHTTPClient.DoTimeout(Request, Response, time.Minute)
switch err {
case fasthttp.ErrTimeout, fasthttp.ErrDialTimeout:
<-time.After(time.Minute * 2)
continue
case fasthttp.ErrNoFreeConns:
<-time.After(time.Minute * 2)
continue
case nil:
return nil
default:
if strings.Contains(err.Error(), "connection reset by peer") {
<-time.After(time.Minute * 2)
continue
} else {
return err
}
}
}
}
But what I truly need to test is sending a request from my object, which implements the same FastRequest method in goroutine.
And here I've got this error message:
error when serving connection ":8080"<->":51325": error when reading request headers: invalid header key " http/1.1\r\nuser-Agent". Buffer size=206, contents: "GET here_is_request_url \n http/1.1\r\nuser-Agent: fasthttp\r\nHost: localhost:8080\r\n\r\n"
In FastRequest I haven't specified any user agent and the functions FastRequest() are the same. Only the place where the function is called is different. Whether it's called in goroutine or not does not matter.
So, fasthttp.RequestCtx cannot parse its own header? or what is going on?
==========================================================================
Also, I should have added that in first case I've used fasthttp v1.6.0, when I changed it to 1.8.0 the error was:
error when serving connection ":8080"<->":57093": error when reading request headers: invalid header name. Buffer size=215, contents: "GET here_is_request_url\n HTTP/1.1\r\nUser-Agent: fasthttp\r\nHost: localhost:8080\r\n\r\n"
And finally, the issue was in "/n" added at the end of the url, that works for real servers, by my small localhost server couldn't handle it.
... contents: "GET here_is_request_url \n http/1.1\r\n ...
^^^^^^^^^^^^^
WRONG!
The URL you use in your code likely has a newline character (\n) still at the end since this is included in your request before the HTTP version and thus messes up the HTTP request. A real URL should have no white space which includes spaces and also newline characters.
Additionally the HTTP version should be all-uppercase HTTP/1.1, i.e. your lower-case http/1.1 is wrong too. You don't show how you create the HTTP request but it is very likely messed up.

No response from gorilla/rpc JSON RPC Service

I'm investigating using the gorilla web toolkit to create a simple RPC API. I'm using the example from their documentation and I'm testing using Advanced Rest Client in Chrome and use
http://localhost:1111/api/
and POST the following RAW JSON payload:
{"method":"HelloService.Say","params":[{"Who":"Test"}]}
This reaches the server, I know this as I'm logging it (see code below) and I get a 200 OK response. However I'm getting "Response does not contain any data"
I'm expecting the JSON reply message that is defined in the Say method below. Does anyone have any suggestions as to what the problem is?
package main
import (
"gorilla/mux"
"gorilla/rpc"
"gorilla/rpc/json"
"log"
"net/http"
)
type HelloArgs struct {
Who string
}
type HelloReply struct {
Message string
}
type HelloService struct{}
func (h *HelloService) Say(r *http.Request, args *HelloArgs, reply *HelloReply) error {
log.Printf(args.Who)
reply.Message = "Hello, " + args.Who + "!"
log.Printf(reply.Message)
return nil
}
func main() {
r := mux.NewRouter()
jsonRPC := rpc.NewServer()
jsonCodec := json.NewCodec()
jsonRPC.RegisterCodec(jsonCodec, "application/json")
jsonRPC.RegisterCodec(jsonCodec, "application/json; charset=UTF-8") // For firefox 11 and other browsers which append the charset=UTF-8
jsonRPC.RegisterService(new(HelloService), "")
r.Handle("/api/", jsonRPC)
http.ListenAndServe(":1111", r)
}
It's because gorilla/rpc/json implements JSON-RPC, which requires three parameters in the request: method, params and id.
Requests without id in JSON-RPC are called notifications and do not have responses.
Check specification for more details.
So, in your case, you need to use following JSON:
{"method":"HelloService.Say","params":[{"Who":"Test"}], "id":"1"}

Resources