I'm using Go on GAE. One of my tasks is sending an email. I am using this documentation successfully to test.
However, what if I do not want a request handler:
func confirm(w http.ResponseWriter, r *http.Request) {
c := appengine.NewContext(r)
...
But rather, send the email through a local func:
func confirm() {
...
Is there any way in Go to do this with GAE? I think what Im looking for is a way to call:
c := appengine.NewContext(r)
But without a request handler, or bypass it in any way possible (which Im reading is probably not possible)
I am thinking a work around could be making an http request from my application to my application - but wow is that ugly!
resp, err := http.Get("http://localhost:8080/sendMail?to=...")
NOTE: After attempting this -- ugly work around I get:
http.DefaultTransport and http.DefaultClient are not available in App Engine. See https://cloud.google.com/appengine/docs/go/urlfetch/
So this workaround is in fact NOT a work around in that urlfetch which is how GAE uses http.GET once again... requires c := appengine.NewContext(r)
You need an appengine.Context to interact with external services, including email and urlfetch. You will have to pass the Context instance into your confirm function.
Related
I'm trying to use Firestore in my AppEngine (standard environment) app written in Go. I've been following the "Getting Started with Cloud Firestore" guide and have been using the firestore package documentation to implement a simple example that works fine when running it on my local dev server.
However when I deploy the app and try the deployed version the call to DocumentRef.Set() fails with the error
rpc error: code = Unavailable desc = all SubConns are in TransientFailure
This is my code that reproduces the issue:
func init() {
http.HandleFunc("/test", testHandler)
}
type testData struct {
TestData string `firestore:"myKey,omitempty"`
}
func testHandler(w http.ResponseWriter, r *http.Request) {
ctx := appengine.NewContext(r)
var firestoreClient *firestore.Client
var firebaseApp *firebase.App
var err error
conf := &firebase.Config{ProjectID: "my-project"}
firebaseApp, err = firebase.NewApp(ctx, conf)
if err != nil {
fmt.Fprintf(w, "Failed to create a new firestore app: %v", err)
return
}
firestoreClient, err = firebaseApp.Firestore(ctx)
if err != nil {
fmt.Fprintf(w, "Failed to create a new firestore client: %v", err)
return
}
data := testData{"my value"}
_, err = firestoreClient.Collection("testCollection").Doc("testDoc").Set(ctx, data)
if err != nil {
fmt.Fprintf(w, "Failed to create a firestore document: %v", err)
return
}
firestoreClient.Close()
fmt.Fprint(w, "Data stored in Firestore successfully")
}
As mentioned before, on the dev server this works fine. So there the returned page contains the text Data stored in Firestore successfully.
When running the deployed code I get Failed to create a firestore document: rpc error: code = Unavailable desc = all SubConns are in TransientFailure instead. Why do I get this error and how can I avoid it?
I've raised an issue about this in the Firestore client library issue tracker and it seems like the situation is a bit complex.
When using App Engine the Firestore client library's network connections goes trough the App Engine socket library. However sockets is only available for paid App Engine apps:
Sockets are only available for paid apps, and traffic from sockets is billed as outgoing bandwidth. Sockets are also limited by daily and per minute (burst) quotas.
So this is the reason why the Firestore client library fails. For small scale projects it's possible to enable billing of your App Engine app and still stay within the free range. If billing is enabled it should work when the app is deployed as well.
However if you are living within the European Union you are not allowed to have a paid App Engine app for non commercial purposes due to Google policies:
If you are located in the European Union and the sole purpose for which you want to use Google Cloud Platform services has no potential economic benefit you should not use the service. If you have already started using Google Cloud Platform, you should discontinue using the service. See Create, modify, or close your billing account to learn how to disable billing on your projects.
So if you are in Europe or for some other reason are unable to use have a paid App Engine app you will not be able to use the Firestore client library.
One alternative in this case is to use the Firestore REST API instead and manually make HTTP requests to Firestore. It's a bit more work, but for smaller scale projects it works.
On AppEngine you need to create a client that uses Http client provided by urlfetch service.
The firestore.NewClient() function accepts ClientOptions parameters that you can create using WithHTTPCLient() function.
Here is an article on issuing HTTP requests from AppEngine Go.
That should help.
So, I have a golang application and reactJs application. If I run them on the different domains, I get problems with CORS.
To allow CORS on the server side is not a problem at all, but I can't get cookies from the server, because they are ignored by browser as third-party cookies.
So, I see quite a simple solution - to run server and frontend on the same domain, but I can't understand, how to do that. How could I configure everything to run on the same domain without conflicts? What are the best practices to prevent CORS between golang app and ReactJS app?
Use Go's file server to serve the static assets:
package main
import (
"log"
"net/http"
)
func main() {
http.HandleFunc("/api/", apiHandler)
http.Handle("/", http.FileServer(http.Dir("/var/www/my-app")))
log.Fatal(http.ListenAndServe(":3000", nil))
}
func apiHandler(w http.ResponseWriter, r *http.Request) {}
The documentation has more examples for http.FileServer.
Alternatively, you can use something like github.com/jteeuwen/go-bindata (and go-bindata-assetfs) to compile your assets into the Go binary and serve them from memory.
Background: I'm running Go on GAE and using Mux for my router. In order to fetch a URL GAE requires that I use its built in urlFetch capability. I want to make this URL fetch happen during my modules init() but as far as I can tell I can only use urlFetch when invoked via a handler.
func init() {
r := mux.NewRouter()
r.HandleFunc("/", homeHandler)
r.HandleFunc("/about", anotherHandler)
http.Handle("/", r)
}
GAE suggests the following code for making a urlFetch:
c := appengine.NewContext(r)
client := urlfetch.Client(c)
... but its argument is an http router, and it doesn't want to work if I pass my mux router. So I'm out of ideas of how to make this urlFetch happen outside the scope of a URL handler.
Error when passing the mux router: "cannot use r (type *mux.Router) as type *http.Request in argument to "appengine".NewContext"
You can't use AppEngine services that require a Context outside of handlers (because the creation of a Context requires an *http.Request value). This by nature means you can't use them in package init() functions either.
Note that you can use them from cron jobs and tasks added to task queues, because tasks and cron jobs are executed by issuing HTTP GET requests.
You have to restructure your code so that the service (urlFetch in your case) gets called from a handler.
A possible solution is to check if init completed in handlers that serve user requests. If not, perform the initialization function you would otherwise put in init() before proceeding to serve the request.
Yes, this may cause first requests to take considerably longer to serve. For this purpose (to avoid this) I recommend you to utilize Warmup requests. A warmup request is issued to a new instance before it goes "live", before it starts serving user requests. In your app.yaml config file you can enable warmup requests by adding -warmup to the inbound_services directive:
inbound_services:
- warmup
This will cause the App Engine infrastructure to first issue a GET request to /_ah/warmup. You can register a handler to this URL and perform initialization tasks. As with any other request, you will have an http.Request in the warmup handler.
But please note that:
..you may encounter loading requests, even if warmup requests are enabled in your app.
Which means that in rare cases it may happen a new instance will not receive a warmup request, so its best to check initialization state in user handlers too.
I've been trying to implement OAuth2 for Go with App engine server to server request.
Here is the code (following the example):
oauthConf, err := google.NewServiceAccountJSONConfig(
"./key.json",
"https://www.googleapis.com/auth/adsense.readonly",
)
if err != nil {
log.Fatal(err)
}
client := http.Client{Transport: oauthConf.NewTransport()}
resp, err := client.Get(urlStr)
...
And I get the error message:
http.DefaultTransport and http.DefaultClient are not available in App Engine.
I'm pretty sure the json.key file is valid as for the other stuff
Googling lead me to understand that it is preferred to use urlfetch, but I can't figure out how to make the setup with the oauth2 config.
The NewServiceAccountJSONConfig returns a oauth2.JWTConfig
https://github.com/golang/oauth2/blob/master/google/google.go#L69-L87
Its .NewTransport() you are using:
https://github.com/golang/oauth2/blob/master/jwt.go#L86-L88
Defaults to the http.DefaultTransport, that is not supported on appengine:
https://github.com/golang/oauth2/blob/master/jwt.go#L163-L168
I would go for using the AppEnigneConfig instead (if possible). See http://godoc.org/github.com/golang/oauth2/google
c := appengine.NewContext(nil)
config := google.NewAppEngineConfig(c, "https://www.googleapis.com/auth/bigquery")
// The following client will be authorized by the App Engine
// app's service account for the provided scopes.
client := http.Client{Transport: config.NewTransport()}
client.Get("...")
Otherwise if you need to use ServiceAccountJSONConfig, you probably can, but you'll need to use urlfetch Client and Transport. Have a look how AppEngineConfig is set up: https://github.com/golang/oauth2/blob/master/google/appengine.go
I wonder know, if Google App Engine's service URL Fetch uses proxy cache, which is discussed in other thread? My question is: if I send request using URL Fetch from my app on GAE to my app (to some handler), will the result be cached in this proxy?
Thanks.
Set an appropriate Cache-control header on URLFetch:
Python
result = urlfetch.fetch(url, headers = {'Cache-Control' : 'max-age=0, must-revalidate'})
GO
client := urlfetch.Client(c)
req, err := http.NewRequest("GET", check.Url, nil)
req.Header.Add("Cache-Control", `max-age=0, must-revalidate`)
resp, err := client.Do(req)
This will work as long as you set the Cache-Control header and you are not requesting the same url (i.e., url foo from foo).
However I would recommend to cache the response of the desired webpages using memcache instead. This will be much faster than calling urlfetch and will give you better control and guarantees.
I'm just speculating on your application's design here, but why would you need to use the UrlFetch API to retrieve something you could easily get instantiating and using your business logic classes directly?
Also note this from the Urlfetch documentation:
To prevent an app from causing an endless recursion of requests, a
request handler is not allowed to fetch its own URL.
If you are really forced to do this, then set the proper Cache-Control header on your request:
headers = {'Cache-Control':'no-cache,max-age=0', 'Pragma':'no-cache'}
result = urlfetch.fetch("http://foo/route", None, urlfetch.GET, headers)
Have a look to this Issue.