Trying to write a PDF directly to the response header in my API - response-headers

I'm using golang:
w.Header().Set("Content-Type", "application/pdf")
w.WriteHeader(200)
err = a.Core.WritePDFToHeader(w) ...
I'm generating the pdf then writing it directly to the response writer. It's hitting my API and there's no errors.
When I call my API I'm getting a correct response back:
Content-Length: 1122
Content-Type: application/pdf
Date: XXXXXXXX
Vary: Origin
But it's not actually 'downloading' in the browser, I can see the raw data in my response:
%PDF-1.3 3 0 obj <</Type /Page /Parent 1 0 R /Resources 2 0 R /Contents 4 0 R>> endobj 4 0 obj
I'm out of ideas as to why this wouldn't work, I hate anything origin related but I have a funny feeling it may be something to do with the origin config. If you have any suggestions please let me know.

No, you don't need it, maybe something else is wrong. That should be enough.
package main
import (
"io/ioutil"
"log"
"net/http"
"os"
)
func main() {
file_name := os.Args[1]
fileD, err := os.Open(file_name)
if err != nil {
log.Panic(err)
}
file_bytes, err := ioutil.ReadAll(fileD)
if err != nil {
log.Panic(err)
}
http.HandleFunc("/pdf_file", func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("content-type", "application/pdf")
w.Write(file_bytes)
})
log.Fatal(http.ListenAndServe(":8080", nil))
}
this sends a pdf file in the browser. Maybe you send row pdf as a string that means encoding is wrong so a browser cannot parse that.

Related

How to handle superfluous response.WriteHeader call in order to return 500

I am aware that the WriteHeader method of http.ResponseWriter can only be called once per HTTP response, can only have a single response status code and can only send the headers once. This is all perfectly fine.
The question is, how should I refactor my code in order to override 201 and return 500 if http.ResponseWriter.Write returns an error? As you can see below I force panic on purpose to see how httprouter.Router.PanicHandler handles it. As expected, logs show http: superfluous response.WriteHeader call from ... and the response is 201 because it is too late as explained above.
package server
import (
"github.com/julienschmidt/httprouter"
"log"
"net/http"
)
func Serve() {
rtr := httprouter.New()
rtr.GET("/", home.Welcome)
handle500(rtr)
err := http.ListenAndServe(":8080", rtr)
if err != nil {
log.Fatalf("server crash")
}
}
func handle500(r *httprouter.Router) {
r.PanicHandler = func(res http.ResponseWriter, req *http.Request, err interface{}) {
res.WriteHeader(http.StatusInternalServerError)
// http: superfluous response.WriteHeader call from line above
}
}
package home
import (
"github.com/julienschmidt/httprouter"
"net/http"
)
func Welcome(res http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
// doing a few bits and building the body
res.Header().Set("Content-Type", "application/json")
res.WriteHeader(201)
_, err := res.Write("body goes here")
if err == nil { // I am doing this deliberately to test 500
panic("assume that something has gone wrong with res.Write and an error occurred")
}
}
There is no way to "override" the status code as it's sent to the browser immediately.
You're checking for the return value of http.ResponseWriter.Write(). I'm not sure this is a good strategy. If writing the response failed then chances are that writing more will probably fail, too.
Logging the failure seems more appropriate, but I would expect most failures to be dropped connections and other errors which won't require action.

HTTP PUT request handler using go

Please consider this sample Go code snippet,
package main
import (
"fmt"
"log"
"net/http"
"time"
)
func main() {
listen_at := ":3114"
go http.Handle("/", http.FileServer(http.Dir(".")))
//go http.Handle("/max", http.FileServer(http.Dir("."))) <-- Fails
go http.HandleFunc("/ping", ping_handler)
log.Fatal(http.ListenAndServe(listen_at, nil))
}
func ping_handler(w http.ResponseWriter, r *http.Request) {
a := time.Now()
layout := "2006-01-02-03-04-05-000"
fmt.Println("Got root!")
fmt.Println("r is", r)
fmt.Println("RemoteAddr is", r.RemoteAddr)
send_this := "OK GOT ping! " + a.Format(layout)
w.Write([]byte(send_this))
}
I've two questions:
(1) How can I change the FileServer to serve /max instead of / - my attempts failed, I get 404 for http://localhost:3114/max/ and http://localhost:3114/max.
(2) I wish to accept PUT requests to /max - how can I achieve this?
Please point me the right direction, thanks!
Edit 1
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
go http.HandleFunc("/ping", hello)
log.Fatal(http.ListenAndServe(":3114", nil))
}
func hello(w http.ResponseWriter, r *http.Request) {
fmt.Println("STarting hello!")
log.Println("Got connection")
if r.URL.Path != "/ping" {
http.Error(w, "404 not found", http.StatusNotFound)
return
}
log.Println("Method:", r.Method)
switch r.Method {
case "GET":
send_this := "OK GOT ping! "
w.Write([]byte(send_this))
case "PUT":
fmt.Println("We got put!")
err := r.ParseForm()
checkErr(err)
fmt.Println("r now", r)
fmt.Println("r.Form", r.Form)
fmt.Println("r.PostForm", r.PostForm)
default:
send_this := "Please dont!"
w.Write([]byte(send_this))
fmt.Fprintf(w, "Unknown request!")
}
}
func checkErr(err error) {
if err != nil {
fmt.Println("Error", err)
}
}
I send a PUT request as curl -k http://localhost:3114/ping -T /tmp/a.go -v, it shows:
STarting hello!
2019/06/07 15:05:10 Got connection
2019/06/07 15:05:10 Method: PUT
We got put!
r now &{PUT /ping HTTP/1.1 1 1 map[Content-Length:[10115] Expect:[100-continue] User-Agent:[curl/7.47.0] Accept:[*/*]] 0xc4200ec2e0 <nil> 10115 [] false localhost:3114 map[] map[] <nil> map[] 127.0.0.1:64612 /ping <nil> <nil> <nil> 0xc42005e340}
r.Form map[]
r.PostForm map[]
How can I find the actual data, and the filename that came in from PUT?
From https://golang.org/pkg/net/http/#ServeMux:
Patterns name fixed, rooted paths, like "/favicon.ico", or rooted
subtrees, like "/images/" (note the trailing slash).
That means that /max, which is a fixed rooted path, will match only /max and the pattern /max/, which is a rooted subtree, will match /max/, any other path that starts with /max/, and by default it will also match /max.
http.Handle("/max/", http.FileServer(http.Dir(".")))
Depending on what the layout of your . directory is, you may need to use https://golang.org/pkg/net/http/#StripPrefix.
Let's say your directory contains two files:
.
├── foo.txt
└── max
└── bar.txt
Given the handler above, a request to /max/bar.txt will return the bar.txt file, but a request to /max/foo.txt or /foo.txt will return 404, no file.
So if you want to serve the files from the /max/ path, but your . directory doesn't have a max sub-directory then you can use StripPrefix to remove the /max prefix from the request's url path before it is passed on to the FileServer.
http.Handle("/max/", http.StripPrefix("/max/", http.FileServer(http.Dir("."))))
To handle PUT requests at the same route you need a custom handler.
type myhandler struct {
fs http.Handler
}
func (h myhandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Method != "PUT" {
// business as usual
h.fs.ServeHTTP(w, r)
return
}
// handle PUT
// ...
}
And then to register it, do:
fs := http.StripPrefix("/max/", http.FileServer(http.Dir(".")))
http.Handle("/max/", myhandler{fs: fs})

Proxying a REST API using golang

I m actually learning golang and try to implement a proxy on a an Rest API that I've made in another language
For now, I only want to query my golang API, extract they actual path parameters and query the other API based on it.
I want the result to be "exactly" the same (or at least, the body part), just like a simple JSON.
For now, I don't want to create a structure for my data, I just want to simply get and retrieve the content.
Here's what I have:
package main
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
)
const API_URL string = "https://my-api-path/"
func setHeaders(w http.ResponseWriter) {
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
}
func extractParams(r *http.Request) map[string]string {
return mux.Vars(r)
}
func getHandler(w http.ResponseWriter, r *http.Request) {
setHeaders(w)
params := extractParams(r)
url := API_URL + params["everything"]
response, err := http.Get(url)
if err != nil {
fmt.Fprint(w, err)
}
fmt.Fprint(w, response)
}
func main() {
router := mux.NewRouter()
router.HandleFunc("/{everything}", getHandler)
http.ListenAndServe(":8080", router)
}
My problem
For now, I m not able to retrieve JSON information from my other API. I only have a text/plain Content-Type which is weird since I enforce application/json and I only have some header details in the response body, something like:
&{200 OK 200 HTTP/2.0 2 0 map[Allow:[GET, HEAD, OPTIONS] Expect-Ct:[max-age=86400, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"] Server:[cloudflare] Cf-Ray:[some-ray] Date:[Tue, 12 Jun 2018 14:38:57 GMT] Content-Type:[application/json] Set-Cookie:[__cfduid=lolol; expires=Wed, 12-Jun-19 14:38:56 GMT; path=/; domain=some-domain; HttpOnly; Secure] Vary:[Accept-Encoding Cookie] X-Frame-Options:[SAMEORIGIN] X-Xss-Protection:[1; mode=block]] 0xc4201926f0 -1 [] false true map[] 0xc420150800 0xc4200e8370}
Do you have any idea on how I can proxy this request (or the JSON result) ?
About the Content-Type header not being written to your response:
Seems to be expected due to the order in which you are performing these operations:
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
See here: https://golang.org/pkg/net/http/#ResponseWriter
Changing the header map after a call to WriteHeader (or Write) has no effect unless the modified headers are trailers.
Try inverting those to read:
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)

How do you send websocket data after a page is rendered in Golang?

I am new to Golang and am trying to send data using web-sockets to a page. I have a handler and I want to be able to serve a file and after it is rendered send it a message. This is some code that I have now.
package main
import (
"github.com/gorilla/websocket"
"log"
"fmt"
"net/http"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
func serveRoot(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "views/index.html")
_, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println(err)
return
}
}
func main() {
http.HandleFunc("/", serveRoot)
fmt.Println("Started")
if err := http.ListenAndServe(":9090", nil); err != nil {
log.Fatal("ListenAndServe:", err)
}
}
The problem is that using the gorilla library I have no idea how to send data and I am getting some output when I load the page.
2018/01/23 08:35:24 http: multiple response.WriteHeader calls
2018/01/23 08:35:24 websocket: the client is not using the websocket protocol: 'upgrade' token not found in 'Connection' header
2018/01/23 08:35:24 http: multiple response.WriteHeader calls
2018/01/23 08:35:24 websocket: 'Origin' header value not allowed
Intention: Send some data after the page is rendered, then (later) hook it up to stdin/stderr
Disclaimer: I am just learning to code, so it would be a great help is you could take that into consideration and not be too vague.
So, as some of the comments mentioned, you can't upgrade a connection that has already been served html. The simple way to do this is just have one endpoint for your websockets, and one endpoint for your html.
So in your example, you might do:
http.HandleFunc("/", serveHtml)
http.HandleFunc("/somethingElse", serveWebsocket)
Where serveHtml has your http.ServeFile call, and serveWebsocket has the upgrading and wotnot.

downloading excelize xlsx file is corrupt

I'm using Excelize library for generating xlsx documents. When I use It's Write(io.writer) func for saving xlsx to file, it works perfectly. But I need to generate and serve this file at web-server. I was trying this solution
func GetConsolidatedReport(w http.ResponseWriter, r *http.Request) {
var reportFile *excelize.File
...
var b bytes.Buffer
writr := bufio.NewWriter(&b)
reportFile.SaveAs("/tmp/testfile.xlsx")
reportFile.Write(writr)
writr.Flush()
fileContents := b.Bytes()
fileSize := strconv.Itoa(len(fileContents))
w.Header().Set("Content-Disposition", "attachment; filename=report.xlsx")
w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Length", fileSize)
t := bytes.NewReader(b.Bytes())
io.Copy(w, t)
}
and after that i've got corrupted zip-file from web-server, but normal file saved in "/tmp/testfile.xlsx"
I've tried content-type as application/zip, application/octet-stream, application/vnd.* but no luck.
Can you help me? Thank you in advance.
PS: By serving i mean on-the-fly generation of file, sorry for any misunderstanding.
PS2: It seems I get an overhead of downloaded file (8085 bytes of original and 13000+ of downloaded) and I can't figure it out where this overhead is come in.
For serving a reader, you should consider http.ServeContent. It will handle the header, content range and so on for you.
Change the line withio.Copy with http.ServeContent(w,r,"testfile.xlsx",time.Now(),t) and it shall work.
Documents: https://golang.org/pkg/net/http/#ServeContent
i use *File.write api and it works:
package main
import (
"net/http"
"github.com/xuri/excelize/v2"
)
func main() {
s := http.Server{
Addr: ":8012",
}
http.HandleFunc("/foo", func(w http.ResponseWriter, r *http.Request) {
f := excelize.NewFile()
// Set value of a cell.
f.SetCellValue("Sheet1", "B2", 100)
w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Disposition", "attachment; filename=example.xlsx")
w.Header().Set("Content-Transfer-Encoding", "binary")
f.Write(w)
})
s.ListenAndServe()
}

Resources