Can I print directly to my (physical, external) printer from Golang, without using printer drivers or CUPS or any other such complexity?
Yes! The following prints a postscript file from Golang using IPP (the internet printing protocol), which most network printers manufactured in the past 20 years support:
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"strconv"
"github.com/alexflint/go-arg"
"github.com/kr/pretty"
"github.com/phin1x/go-ipp"
)
func Main() error {
var args struct {
URI string `arg:"positional,required"`
PostscriptFile string `arg:"positional,required"`
}
arg.MustParse(&args)
// define a ipp request
req := ipp.NewRequest(ipp.OperationPrintJob, 1)
req.OperationAttributes[ipp.AttributeCharset] = "utf-8"
req.OperationAttributes[ipp.AttributeNaturalLanguage] = "en"
req.OperationAttributes[ipp.AttributePrinterURI] = args.URI
req.OperationAttributes[ipp.AttributeRequestingUserName] = "some-user"
req.OperationAttributes[ipp.AttributeDocumentFormat] = "application/octet-stream"
// encode request to bytes
payload, err := req.Encode()
if err != nil {
return fmt.Errorf("error encoding ipp request: %w", err)
}
// read the test page
postscript, err := ioutil.ReadFile(args.PostscriptFile)
if err != nil {
return fmt.Errorf("error reading postscript file: %w", err)
}
payload = append(payload, postscript...)
// send ipp request to remote server via http
httpReq, err := http.NewRequest("POST", args.URI, bytes.NewReader(payload))
if err != nil {
return fmt.Errorf("error creating http request: %w", err)
}
// set ipp headers
httpReq.Header.Set("Content-Length", strconv.Itoa(len(payload)))
httpReq.Header.Set("Content-Type", ipp.ContentTypeIPP)
// perform the request
var httpClient http.Client
httpResp, err := httpClient.Do(httpReq)
if err != nil {
return fmt.Errorf("error executing http request: %w", err)
}
defer httpResp.Body.Close()
// read the response
buf, err := io.ReadAll(httpResp.Body)
if err != nil {
return fmt.Errorf("error reading response body: %w", err)
}
// response must be 200 for a successful operation
// other possible http codes are:
// - 500 -> server error
// - 426 -> sever requests a encrypted connection
// - 401 -> forbidden -> need authorization header or user is not permitted
if httpResp.StatusCode != 200 {
return fmt.Errorf("printer said %d: %s", httpResp.StatusCode, buf)
}
// decode ipp response
resp, err := ipp.NewResponseDecoder(bytes.NewReader(buf)).Decode(nil)
if err != nil {
return fmt.Errorf("error decoding ipp response: %w", err)
}
// print the response
fmt.Println("Submitted print job. Response was:")
pretty.Println(resp)
return nil
}
The URL to use is just http://ip-address-of-myprinter (this was tested on a brother HL-series printer)
Related
I am writing a code in GoLang to get data from a url but when I make http.Get() request, the response I get is as per the following.
"compile_output": "# command-line-arguments\n/usr/local/go-1.13.5/pkg/tool/linux_amd64/link: resize output file failed: truncate $WORK/b001/exe/a.out: file too large\n",
Please suggest me how to tackle this.
Code is as per the following:
import (
"fmt"
"io/ioutil"
"net/http"
)
func main() {
// define the URL to get data from
url := "my_example_url"
// use http.Get() to make a GET request to the URL
resp, err := http.Get(url)
if err != nil {
fmt.Println("Error getting data:", err)
return
}
defer resp.Body.Close()
// read the response body
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading response:", err)
return
}
// print the response body
fmt.Println(string(body))
}
I tried reading through GoLang URl Documentations, but didn't help.
I've got a http.Client in go and I want it to update every content type for every response to application/json (even though it might not be the case) even before it process the response.
Which attribute shall I override?
Context: the underlying issue there's a bug in the third party API where the real content type is application/json but it's set to the other thing (incorrectly).
Code snippet:
...
requestURL := fmt.Sprintf("http://localhost:%d", serverPort)
req, err := http.NewRequest(http.MethodGet, requestURL, nil)
if err != nil {
fmt.Printf("client: could not create request: %s\n", err)
os.Exit(1)
}
res, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Printf("client: error making http request: %s\n", err)
os.Exit(1)
}
fmt.Printf("client: got response!\n")
fmt.Printf("client: status code: %d\n", res.StatusCode)
resBody, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Printf("client: could not read response body: %s\n", err)
os.Exit(1)
}
fmt.Printf("client: response body: %s\n", resBody)
}
package main
import (
"fmt"
"net/http"
)
type MyRoundTripper struct {
httprt http.RoundTripper
}
func (rt MyRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
res, err := rt.httprt.RoundTrip(req)
if err != nil {
fmt.Printf("Error: %v", err)
} else {
res.Header.Set("Content-Type", "application/json")
}
return res, err
}
func main() {
client := &http.Client{Transport: MyRoundTripper{http.DefaultTransport}}
resp, err := client.Get("https://example.com")
if err != nil {
// handle error
}
fmt.Printf("%+v\n", resp.Header)
}
I am trying to stream JSON text from a websocket. However after an initial read I noticed that the stream seems to break/disconnect. This is from a Pleroma server (think: Mastodon). I am using the default Golang websocket library.
package main
import (
"bufio"
"fmt"
"log"
"golang.org/x/net/websocket"
)
func main() {
origin := "https://poa.st/"
url := "wss://poa.st/api/v1/streaming/?stream=public"
ws, err := websocket.Dial(url, "", origin)
if err != nil {
log.Fatal(err)
}
s := bufio.NewScanner(ws)
for s.Scan() {
line := s.Text()
fmt.Println(line)
}
}
After the initial JSON text response, the for-loop breaks. I would expect it to send a new message every few seconds.
What might be causing this? I am willing to switch to the Gorilla websocket library if I can use it with bufio.
Thanks!
Although x/net/websocket connection has a Read method with the same signature as the Read method in io.Reader, the connection does not work like an io.Reader. The connection will not work as you expect when wrapped with a bufio.Scanner.
The poa.st endpoint sends a stream of messages where each message is a JSON document. Use the following code to read the messages using the Gorilla package:
url := "wss://poa.st/api/v1/streaming/?stream=public"
ws, _, err := websocket.DefaultDialer.Dial(url, nil)
if err != nil {
log.Fatal(err)
}
defer ws.Close()
for {
_, p, err := ws.ReadMessage()
if err != nil {
log.Fatal(err)
}
// p is a []byte containing the JSON document.
fmt.Printf("%s\n", p)
}
The Gorilla package has a helper method for decoding JSON messages. Here's an example of how to use that method.
url := "wss://poa.st/api/v1/streaming/?stream=public"
ws, _, err := websocket.DefaultDialer.Dial(url, nil)
if err != nil {
log.Fatal(err)
}
defer ws.Close()
for {
// The JSON documents are objects containing two fields,
// the event type and the payload. The payload is a JSON
// document itself.
var e struct {
Event string
Payload string
}
err := ws.ReadJSON(&e)
if err != nil {
log.Fatal(err)
}
// TODO: decode e.Payload based on e.Event
}
I'm having a client-server code for http2, I want to send file from client to server. But I'm stuck on how to do that. Means how to break my file in small chucks and send it via that connection.
Any help and link will be appreciated.
Thanks
//http2 client
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"golang.org/x/net/http2"
)
func main() {
client := http.Client{
// InsecureTLSDial is temporary and will likely be
// replaced by a different API later.
Transport: &http2.Transport{InsecureTLSDial: true},
}
// Perform the request
resp, err := client.Post("https://localhost:9191/hello/sayHello", "text/plain", bytes.NewBufferString("Hello Go!"))
if err != nil {
log.Fatalf("Failed get: %s", err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatalf("Failed reading response body: %s", err)
}
fmt.Printf("Got response %d: %s %s", resp.StatusCode, resp.Proto, string(body))
}
I wrote a little web crawler and had known that the Response is a zip file.
In my limited experience with golang programing, I only know how to unzip a existing file.
Can I unzip the Response.Body in memory without saving it in hard disk in advance?
Updating answer for handling Zip file response body in-memory.
Note: Ensure you have sufficient memory for handling zip file.
package main
import (
"archive/zip"
"bytes"
"fmt"
"io/ioutil"
"log"
"net/http"
)
func main() {
resp, err := http.Get("zip file url")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
zipReader, err := zip.NewReader(bytes.NewReader(body), int64(len(body)))
if err != nil {
log.Fatal(err)
}
// Read all the files from zip archive
for _, zipFile := range zipReader.File {
fmt.Println("Reading file:", zipFile.Name)
unzippedFileBytes, err := readZipFile(zipFile)
if err != nil {
log.Println(err)
continue
}
_ = unzippedFileBytes // this is unzipped file bytes
}
}
func readZipFile(zf *zip.File) ([]byte, error) {
f, err := zf.Open()
if err != nil {
return nil, err
}
defer f.Close()
return ioutil.ReadAll(f)
}
By default Go HTTP client handles Gzip response automatically. So do typical read and close of response body.
However there is a catch in it.
// Reference https://github.com/golang/go/blob/master/src/net/http/transport.go
//
// DisableCompression, if true, prevents the Transport from
// requesting compression with an "Accept-Encoding: gzip"
// request header when the Request contains no existing
// Accept-Encoding value. If the Transport requests gzip on
// its own and gets a gzipped response, it's transparently
// decoded in the Response.Body. However, if the user
// explicitly requested gzip it is not automatically
// uncompressed.
DisableCompression bool
What it means is; If you add a header Accept-Encoding: gzip manually in the request then you have to handle Gzip response body by yourself.
For Example -
reader, err := gzip.NewReader(resp.Body)
if err != nil {
log.Fatal(err)
}
defer reader.Close()
body, err := ioutil.ReadAll(reader)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(body))