Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure the services status page renders as HTML #1265

Closed
wants to merge 3 commits into from

Conversation

simonswine
Copy link
Contributor

What this PR does

This removes the text/plain content type header from the services page

Which issue(s) this PR fixes or relates to

This ensures the services page of Mimir renders as HTML. I have noticed that when testing the v2.0.0-rc.0

scrn-2022-02-22-13-02-32

Checklist

  • [ ] Tests updated
  • [ ] Documentation added
  • [ ] CHANGELOG.md updated - the order of entries should be [CHANGE], [FEATURE], [ENHANCEMENT], [BUGFIX]

@simonswine simonswine added bug Something isn't working type/bug labels Feb 22, 2022
@56quarters
Copy link
Contributor

What content type gets set instead? Or is the browser just guessing?

@osg-grafana
Copy link
Contributor

In "https://github.com/grafana/mimir/blob/204a2a316708dba2e7f70736e65bc1241a82fabd/pkg/mimir/status.go#L22"
"Services Status", is it singular or plural. This needs to be sentence case, but could be written three ways:

  • Service’s status [only seeing status information about one service]
  • Services’ status [seeing status information about more than one service]
  • Service status [I would not recommend this version, because "Service" can look like a verb or a noun]

@simonswine
Copy link
Contributor Author

  • Services’ status [seeing status information about more than one service]

This is the one

What content type gets set instead? Or is the browser just guessing?

The browser is guessing. There is also a chance that JSON is requested and returned. But I think text/plain is just always wrong.

@pstibrany
Copy link
Member

pstibrany commented Feb 23, 2022

Oh wait, we should not let browser guess. I thought we set the content type in util.RenderHTTPResponse. If we don't, can we do that? (RenderHTTPResponse actually sets application/json for JSON, but doesn't set text/html when writing HTML. If it cannot just set text/html, perhaps it needs extra parameter with correct content type in the case it's not rendering json.)

@simonswine simonswine force-pushed the 20220222_fix-content-type-status branch from 3b56223 to f8588ac Compare March 7, 2022 18:40
@simonswine
Copy link
Contributor Author

Oh wait, we should not let browser guess. I thought we set the content type in util.RenderHTTPResponse. If we don't, can we do that? (RenderHTTPResponse actually sets application/json for JSON, but doesn't set text/html when writing HTML. If it cannot just set text/html, perhaps it needs extra parameter with correct content type in the case it's not rendering json.)

Looking another time at this: I think the browser never guessed. If nothing is set something in our stack seems to set it to text/html; charset=utf-8. I have also observed that the json content-type never worked, as we call WriteHeader(200)
before we overwrite it.

So my proposed change is to remove WriteHeader(200), because that happens anyhow, when we first Write.

Looking at the content-types before this PR:

# HTML
$ curl -s -D - -o /dev/null http://127.0.0.1:8080/services | grep -i content-type:
Content-Type: text/html; charset=utf-8

# JSON
$ curl -s -H "accept: application/json" -D - -o /dev/null http://127.0.0.1:8080/services | grep -i content-type:                                                                                                                                                                                                                                                                      
Content-Type: text/plain; charset=utf-8

# This is how my firefox browser requests the page
$ curl 'http://127.0.0.1:8080/services' \
  -H 'Connection: keep-alive' \
  -H 'Cache-Control: max-age=0' \
  -H 'sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="98"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-platform: "Linux"' \
  -H 'Upgrade-Insecure-Requests: 1' \
  -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36' \
  -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9' \
  -H 'Sec-Fetch-Site: same-origin' \
  -H 'Sec-Fetch-Mode: navigate' \
  -H 'Sec-Fetch-User: ?1' \
  -H 'Sec-Fetch-Dest: document' \
  -H 'Referer: http://127.0.0.1:8080/' \
  -H 'Accept-Language: en-US,en;q=0.9,de;q=0.8' \
  --compressed \
  -D - \
  -s \
  -o /dev/null | grep -i content-type
Content-Type: text/plain

And after:

# HTML
$ curl -s -D - -o /dev/null http://127.0.0.1:8080/services | grep -i content-type:
Content-Type: text/html; utf=8

# JSON
$ curl -s -H "accept: application/json" -D - -o /dev/null http://127.0.0.1:8080/services | grep -i content-type:
Content-Type: application/json

# This is how my firefox browser requests the page
$ curl 'http://127.0.0.1:8080/services' \                                                                                                                                                                                                                                                                                                                                               
  -H 'Connection: keep-alive' \
  -H 'Cache-Control: max-age=0' \
  -H 'sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="98"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-platform: "Linux"' \
  -H 'Upgrade-Insecure-Requests: 1' \
  -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36' \
  -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9' \
  -H 'Sec-Fetch-Site: same-origin' \
  -H 'Sec-Fetch-Mode: navigate' \
  -H 'Sec-Fetch-User: ?1' \
  -H 'Sec-Fetch-Dest: document' \
  -H 'Referer: http://127.0.0.1:8080/' \
  -H 'Accept-Language: en-US,en;q=0.9,de;q=0.8' \
  --compressed \
  -D - \
  -s \
  -o /dev/null | grep -i content-type                                                                                                                                                                                                                                                                                                                                                            
Content-Type: text/html; utf=8

This seems to fix all of those cases.

@simonswine simonswine requested review from pstibrany and pracucci March 7, 2022 18:53
Comment on lines -59 to +58
w.Header().Set("Content-Type", "text/plain")
w.Header().Set("Content-Type", "text/html; utf=8")
Copy link
Contributor

@colega colega Mar 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

util.RenderHTTPResponse will try to set the content-type to application/json if that was requested, but we've already sent a content-type here. Why do we need this content-type setting at all?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RenderHTTPResponse only sets JSON content type. I think we should pass "non-JSON" content type to it, so that RenderHTTPResponse is always responsible for setting it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just checked the response for the /store-gateway/tenants, for example, which does not explicitly write the header, and the response contains the text/html content-type:

curl -I -XGET localhost:8080/store-gateway/tenants
HTTP/1.1 200 OK
Vary: Accept-Encoding
Date: Tue, 22 Mar 2022 09:22:40 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked

So there's some kind of magic happening somewhere, I can recall that Golang detected the content type based on what the output looks like.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, see doc for Write method of ResponseWriter: https://pkg.go.dev/net/http#ResponseWriter

If the Header does not contain a Content-Type line, Write adds a Content-Type set to the result of passing the initial 512 bytes of written data to DetectContentType.

I don't think we should rely on it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should rely on it.

I think it's not critical and we can expect it to work properly when we're sending HTML, but if not, sure, we can always specify it explicitly.

Anyway, my main comment was that we shouldn't set the content type here prematurely.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anyway, my main comment was that we shouldn't set the content type here prematurely.

Agree.

@@ -396,7 +396,7 @@ func (a *API) RegisterQueryScheduler(f *scheduler.Scheduler) {
// TODO: Refactor this code to be accomplished using the services.ServiceManager
// or a future module manager #2291
func (a *API) RegisterServiceMapHandler(handler http.Handler) {
a.indexPage.AddLink(SectionAdminEndpoints, "/services", "Service Status")
a.indexPage.AddLink(SectionAdminEndpoints, "/services", "Service's status")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be rebased.

@colega
Copy link
Contributor

colega commented Mar 30, 2022

I started from scratch here: #1575
I'll address the page title in a separate PR once #1572 is merged.

I think we can close this.

@pracucci pracucci closed this Mar 31, 2022
@colega colega deleted the 20220222_fix-content-type-status branch March 31, 2022 08:52
colega added a commit that referenced this pull request Mar 31, 2022
As Ursula pointed out, this page is listing the status information about
more than one service, so this is the correct spelling.

See: #1265 (comment)

Signed-off-by: Oleg Zaytsev <[email protected]>
@colega colega mentioned this pull request Mar 31, 2022
3 tasks
colega added a commit that referenced this pull request Mar 31, 2022
* Fix "Services' status" spelling

As Ursula pointed out, this page is listing the status information about
more than one service, so this is the correct spelling.

See: #1265 (comment)

Signed-off-by: Oleg Zaytsev <[email protected]>

* Also fix the section header

Signed-off-by: Oleg Zaytsev <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working type/bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants